@@ -105,12 +105,11 @@ def _instrument_streaming_body(
105105 if isinstance (span , StreamedSpan ):
106106 streaming_span = sentry_sdk .traces .start_span (
107107 name = span .name ,
108- # `parent_span` is set explicitly to the boto span.
108+ # keep stream span under the boto span after `_make_api_call()` returns .
109109 parent_span = span ,
110- # avoid making the streaming span the current span on the scope since the application might
111- # keep `StreamingBody` open before reading it. Otherwise: 1. when the streamingspan ends it
112- # could restore the parent span on the scope, breaking the parent-child relation of newly
113- # created spans; 2. newly created spans would be attached to the streaming span.
110+ # the body may outlive the api call, so keep it inactive. Otherwise it
111+ # 1. could restore the already-finished boto span when it ends; 2. make
112+ # unrelated new spans attach to the stream span since it's the current span.
114113 active = False ,
115114 attributes = {
116115 "sentry.op" : OP .HTTP_CLIENT_STREAM ,
@@ -133,6 +132,7 @@ def finish_span(error: "Optional[BaseException]" = None) -> None:
133132 return
134133
135134 finished = True
135+ # finish stream span before boto span, and only once across read/close.
136136 _finish_span (streaming_span , error )
137137 _finish_span (span , error )
138138
@@ -152,6 +152,7 @@ def sentry_streaming_body_read(*args: "Any", **kwargs: "Any") -> bytes:
152152 read_return_value = orig_read (* args , ** kwargs )
153153 with capture_internal_exceptions ():
154154 amount_of_bytes_requested = args [0 ] if args else kwargs .get ("amt" )
155+ # detect read-to-end, eof, or the known content length being consumed.
155156 if (
156157 amount_of_bytes_requested is None
157158 or amount_of_bytes_requested < 0
@@ -189,8 +190,6 @@ def sentry_raw_stream_close(*args: "Any", **kwargs: "Any") -> None:
189190 raw_stream = body ._raw_stream # type: ignore[attr-defined]
190191 orig_raw_close = raw_stream .close
191192
192- # StreamingBody.__exit__ closes `_raw_stream` directly, bypassing
193- # StreamingBody.close(), so both levels need to be instrumented.
194193 raw_stream .close = sentry_raw_stream_close
195194 body .read = sentry_streaming_body_read # type: ignore
196195 body .close = sentry_streaming_body_close # type: ignore
0 commit comments