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