From 5d88ac901e5899670be1611347c36f9543a05afb Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 09:16:22 +0200 Subject: [PATCH 1/7] chore(stdlib): Remove transaction-based tracing --- sentry_sdk/integrations/stdlib.py | 184 ++-- tests/integrations/stdlib/test_httplib.py | 915 ++++++------------- tests/integrations/stdlib/test_subprocess.py | 436 +++------ 3 files changed, 464 insertions(+), 1071 deletions(-) diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py index cb31d90785..bd33a63167 100644 --- a/sentry_sdk/integrations/stdlib.py +++ b/sentry_sdk/integrations/stdlib.py @@ -15,7 +15,6 @@ EnvironHeaders, add_http_breadcrumb, add_http_request_source, - has_span_streaming_enabled, should_propagate_trace, ) from sentry_sdk.utils import ( @@ -112,73 +111,43 @@ def putrequest( with capture_internal_exceptions(): parsed_url = parse_url(real_url, sanitize=False) - span_streaming = has_span_streaming_enabled(client.options) - span: "Union[Span, StreamedSpan, None]" = None + span: "Optional[StreamedSpan]" = None breadcrumb: "dict[str, Any]" = {} - if span_streaming: - breadcrumb[SPANDATA.HTTP_REQUEST_METHOD] = method - if parsed_url is not None and should_send_default_pii(): - breadcrumb.update( - { - SPANDATA.URL_FRAGMENT: parsed_url.fragment, - SPANDATA.URL_FULL: parsed_url.url, - SPANDATA.URL_QUERY: parsed_url.query, - } - ) - - if sentry_sdk.traces.get_current_span() is not None: - span = sentry_sdk.traces.start_span( - name="%s %s" - % ( - method, - parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE, - ), - attributes={ - "sentry.origin": "auto.http.stdlib.httplib", - "sentry.op": OP.HTTP_CLIENT, - SPANDATA.HTTP_REQUEST_METHOD: method, - }, - ) - - if parsed_url is not None and should_send_default_pii(): - span.set_attribute(SPANDATA.URL_FRAGMENT, parsed_url.fragment) - span.set_attribute(SPANDATA.URL_FULL, parsed_url.url) - span.set_attribute(SPANDATA.URL_QUERY, parsed_url.query) - - set_on_span = span.set_attribute + breadcrumb[SPANDATA.HTTP_REQUEST_METHOD] = method + if parsed_url is not None and should_send_default_pii(): + breadcrumb.update( + { + SPANDATA.URL_FRAGMENT: parsed_url.fragment, + SPANDATA.URL_FULL: parsed_url.url, + SPANDATA.URL_QUERY: parsed_url.query, + } + ) - else: - span = sentry_sdk.start_span( - op=OP.HTTP_CLIENT, + if sentry_sdk.traces.get_current_span() is not None: + span = sentry_sdk.traces.start_span( name="%s %s" - % (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE), - origin="auto.http.stdlib.httplib", + % ( + method, + parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE, + ), + attributes={ + "sentry.origin": "auto.http.stdlib.httplib", + "sentry.op": OP.HTTP_CLIENT, + SPANDATA.HTTP_REQUEST_METHOD: method, + }, ) - span.set_data(SPANDATA.HTTP_METHOD, method) - breadcrumb[SPANDATA.HTTP_METHOD] = method - - if parsed_url is not None: - span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment) - span.set_data("url", parsed_url.url) - span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query) - - breadcrumb.update( - { - SPANDATA.HTTP_FRAGMENT: parsed_url.fragment, - "url": parsed_url.url, - SPANDATA.HTTP_QUERY: parsed_url.query, - } - ) - - set_on_span = span.set_data + if parsed_url is not None and should_send_default_pii(): + span.set_attribute(SPANDATA.URL_FRAGMENT, parsed_url.fragment) + span.set_attribute(SPANDATA.URL_FULL, parsed_url.url) + span.set_attribute(SPANDATA.URL_QUERY, parsed_url.query) # for proxies, these point to the proxy host/port if tunnel_host: if span: - set_on_span(SPANDATA.NETWORK_PEER_ADDRESS, self.host) - set_on_span(SPANDATA.NETWORK_PEER_PORT, self.port) + span.set_attribute(SPANDATA.NETWORK_PEER_ADDRESS, self.host) + span.set_attribute(SPANDATA.NETWORK_PEER_PORT, self.port) breadcrumb.update( { @@ -365,27 +334,16 @@ def sentry_patched_popen_init( data={"subprocess.cwd": cwd} if cwd else {}, ) - span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options) - span: "Union[Span, StreamedSpan]" - if span_streaming: - if sentry_sdk.traces.get_current_span() is None: - return old_popen_init(self, *a, **kw) - - span = sentry_sdk.traces.start_span( - name=description, - attributes={ - "sentry.op": OP.SUBPROCESS, - "sentry.origin": "auto.subprocess.stdlib.subprocess", - }, - ) - else: - span = sentry_sdk.start_span( - op=OP.SUBPROCESS, - name=description, - origin="auto.subprocess.stdlib.subprocess", - ) + if sentry_sdk.traces.get_current_span() is None: + return old_popen_init(self, *a, **kw) - with span: + with sentry_sdk.traces.start_span( + name=description, + attributes={ + "sentry.op": OP.SUBPROCESS, + "sentry.origin": "auto.subprocess.stdlib.subprocess", + }, + ) as span: for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers( span=span ): @@ -399,15 +357,9 @@ def sentry_patched_popen_init( ) env["SUBPROCESS_" + k.upper().replace("-", "_")] = v - if cwd and isinstance(span, Span): - span.set_data("subprocess.cwd", cwd) - rv = old_popen_init(self, *a, **kw) - if isinstance(span, StreamedSpan): - span.set_attribute(SPANDATA.PROCESS_PID, self.pid) - else: - span.set_tag("subprocess.pid", self.pid) + span.set_attribute(SPANDATA.PROCESS_PID, self.pid) return rv @@ -419,26 +371,17 @@ def sentry_patched_popen_init( def sentry_patched_popen_wait( self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any" ) -> "Any": - span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options) - if span_streaming: - if sentry_sdk.traces.get_current_span() is None: - return old_popen_wait(self, *a, **kw) - with sentry_sdk.traces.start_span( - name=OP.SUBPROCESS_WAIT, - attributes={ - "sentry.op": OP.SUBPROCESS_WAIT, - "sentry.origin": "auto.subprocess.stdlib.subprocess", - }, - ) as span: - span.set_attribute(SPANDATA.PROCESS_PID, self.pid) - return old_popen_wait(self, *a, **kw) - else: - with sentry_sdk.start_span( - op=OP.SUBPROCESS_WAIT, - origin="auto.subprocess.stdlib.subprocess", - ) as span: - span.set_tag("subprocess.pid", self.pid) - return old_popen_wait(self, *a, **kw) + if sentry_sdk.traces.get_current_span() is None: + return old_popen_wait(self, *a, **kw) + with sentry_sdk.traces.start_span( + name=OP.SUBPROCESS_WAIT, + attributes={ + "sentry.op": OP.SUBPROCESS_WAIT, + "sentry.origin": "auto.subprocess.stdlib.subprocess", + }, + ) as span: + span.set_attribute(SPANDATA.PROCESS_PID, self.pid) + return old_popen_wait(self, *a, **kw) subprocess.Popen.wait = sentry_patched_popen_wait # type: ignore @@ -448,26 +391,17 @@ def sentry_patched_popen_wait( def sentry_patched_popen_communicate( self: "subprocess.Popen[Any]", *a: "Any", **kw: "Any" ) -> "Any": - span_streaming = has_span_streaming_enabled(sentry_sdk.get_client().options) - if span_streaming: - if sentry_sdk.traces.get_current_span() is None: - return old_popen_communicate(self, *a, **kw) - with sentry_sdk.traces.start_span( - name=OP.SUBPROCESS_COMMUNICATE, - attributes={ - "sentry.op": OP.SUBPROCESS_COMMUNICATE, - "sentry.origin": "auto.subprocess.stdlib.subprocess", - }, - ) as span: - span.set_attribute(SPANDATA.PROCESS_PID, self.pid) - return old_popen_communicate(self, *a, **kw) - else: - with sentry_sdk.start_span( - op=OP.SUBPROCESS_COMMUNICATE, - origin="auto.subprocess.stdlib.subprocess", - ) as span: - span.set_tag("subprocess.pid", self.pid) - return old_popen_communicate(self, *a, **kw) + if sentry_sdk.traces.get_current_span() is None: + return old_popen_communicate(self, *a, **kw) + with sentry_sdk.traces.start_span( + name=OP.SUBPROCESS_COMMUNICATE, + attributes={ + "sentry.op": OP.SUBPROCESS_COMMUNICATE, + "sentry.origin": "auto.subprocess.stdlib.subprocess", + }, + ) as span: + span.set_attribute(SPANDATA.PROCESS_PID, self.pid) + return old_popen_communicate(self, *a, **kw) subprocess.Popen.communicate = sentry_patched_popen_communicate # type: ignore diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index 60485595f3..98a4bf10a9 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -13,7 +13,7 @@ import pytest import sentry_sdk -from sentry_sdk import capture_message, continue_trace, start_transaction +from sentry_sdk import capture_message from sentry_sdk.consts import MATCH_ALL, SPANDATA from sentry_sdk.integrations.stdlib import StdlibIntegration from tests.conftest import ApproxDict, create_mock_http_server, get_free_port @@ -325,10 +325,8 @@ def before_breadcrumb(crumb, hint): ) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_empty_realurl( sentry_init, - span_streaming, ): """ Ensure that after using sentry_sdk.init you can putrequest a @@ -337,7 +335,7 @@ def test_empty_realurl( sentry_init( dsn="", - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) HTTPConnection("localhost", port=PORT).putrequest("POST", None) @@ -391,16 +389,14 @@ def test_httplib_misuse(sentry_init, capture_events, request): ) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_outgoing_trace_headers( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) already_patched_getresponse = HTTPSConnection.getresponse @@ -422,8 +418,6 @@ def send(self, *args, **kwargs) -> None: def getresponse(self, *args, **kwargs): return already_patched_getresponse(self, *args, **kwargs) - events = capture_events() - headers = { "sentry-trace": "771a43a4192642f0b136d5159a501700-1234567890abcdef-1", "baggage": ( @@ -437,83 +431,49 @@ def getresponse(self, *args, **kwargs): "other-vendor-value-2=foo;bar;" ), } + items = capture_items("span") + sentry_sdk.traces.continue_trace(headers) - if span_streaming: - items = capture_items("span") - sentry_sdk.traces.continue_trace(headers) - - with sentry_sdk.traces.start_span( - name="/interactions/other-dogs/new-dog", - attributes={ - "sentry.op": "greeting.sniff", - }, - ): - connection = HTTPSConnectionRecordingRequestHeaders("localhost", port=PORT) - connection.request("GET", "/top-chasers") - connection.getresponse() - - sentry_sdk.flush() - request_span = next(item.payload for item in items) - expected_sentry_trace = "{trace_id}-{parent_span_id}-{sampled}".format( - trace_id=request_span["trace_id"], - parent_span_id=request_span["span_id"], - sampled=1, - ) - - expected_outgoing_baggage = ( - "sentry-trace_id=771a43a4192642f0b136d5159a501700," - "sentry-public_key=49d0f7386ad645858ae85020e393bef3," - "sentry-sample_rate=1.0," - "sentry-user_id=Am%C3%A9lie," - "sentry-sample_rand=0.000005," - "sentry-sampled=true" - ) - else: - events = capture_events() - transaction = continue_trace(headers) - - with start_transaction( - transaction=transaction, - name="/interactions/other-dogs/new-dog", - op="greeting.sniff", - trace_id="12312012123120121231201212312012", - ) as transaction: - connection = HTTPSConnectionRecordingRequestHeaders("localhost", port=PORT) - connection.request("GET", "/top-chasers") - connection.getresponse() - - (event,) = events - request_span = event["spans"][-1] - expected_sentry_trace = "{trace_id}-{parent_span_id}-{sampled}".format( - trace_id=event["contexts"]["trace"]["trace_id"], - parent_span_id=request_span["span_id"], - sampled=1, - ) + with sentry_sdk.traces.start_span( + name="/interactions/other-dogs/new-dog", + attributes={ + "sentry.op": "greeting.sniff", + }, + ): + connection = HTTPSConnectionRecordingRequestHeaders("localhost", port=PORT) + connection.request("GET", "/top-chasers") + connection.getresponse() + + sentry_sdk.flush() + request_span = next(item.payload for item in items) + expected_sentry_trace = "{trace_id}-{parent_span_id}-{sampled}".format( + trace_id=request_span["trace_id"], + parent_span_id=request_span["span_id"], + sampled=1, + ) - expected_outgoing_baggage = ( - "sentry-trace_id=771a43a4192642f0b136d5159a501700," - "sentry-public_key=49d0f7386ad645858ae85020e393bef3," - "sentry-sample_rate=1.0," - "sentry-user_id=Am%C3%A9lie," - "sentry-sample_rand=0.000005," - "sentry-sampled=true" - ) + expected_outgoing_baggage = ( + "sentry-trace_id=771a43a4192642f0b136d5159a501700," + "sentry-public_key=49d0f7386ad645858ae85020e393bef3," + "sentry-sample_rate=1.0," + "sentry-user_id=Am%C3%A9lie," + "sentry-sample_rand=0.000005," + "sentry-sampled=true" + ) assert request_headers["sentry-trace"] == expected_sentry_trace assert request_headers["baggage"] == expected_outgoing_baggage -@pytest.mark.parametrize("span_streaming", [True, False]) def test_outgoing_trace_headers_head_sdk( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=0.5, release="foo", - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) already_patched_getresponse = HTTPSConnection.getresponse @@ -535,69 +495,33 @@ def send(self, *args, **kwargs) -> None: def getresponse(self, *args, **kwargs): return already_patched_getresponse(self, *args, **kwargs) - if span_streaming: - items = capture_items("span") + items = capture_items("span") - sentry_sdk.traces.continue_trace({}) + sentry_sdk.traces.continue_trace({}) - with mock.patch( - "sentry_sdk.tracing_utils.Random.randrange", return_value=250000 - ): - with sentry_sdk.traces.start_span(name="Head SDK tx"): - connection = HTTPSConnectionRecordingRequestHeaders( - "localhost", port=PORT - ) - connection.request("GET", "/top-chasers") - connection.getresponse() - - sentry_sdk.flush() - request_span = next(item.payload for item in items) - expected_sentry_trace = "{trace_id}-{parent_span_id}-{sampled}".format( - trace_id=request_span["trace_id"], - parent_span_id=request_span["span_id"], - sampled=1, - ) - - expected_outgoing_baggage = ( - "sentry-trace_id=%s," - "sentry-sample_rand=0.250000," - "sentry-environment=production," - "sentry-release=foo," - "sentry-transaction=Head%%20SDK%%20tx," - "sentry-sample_rate=0.5," - "sentry-sampled=true" - ) % request_span["trace_id"] - else: - events = capture_events() - - with mock.patch( - "sentry_sdk.tracing_utils.Random.randrange", return_value=250000 - ): - transaction = continue_trace({}) - - with start_transaction( - transaction=transaction, name="Head SDK tx" - ) as transaction: + with mock.patch("sentry_sdk.tracing_utils.Random.randrange", return_value=250000): + with sentry_sdk.traces.start_span(name="Head SDK tx"): connection = HTTPSConnectionRecordingRequestHeaders("localhost", port=PORT) connection.request("GET", "/top-chasers") connection.getresponse() - (event,) = events - request_span = event["spans"][-1] - expected_sentry_trace = "{trace_id}-{parent_span_id}-{sampled}".format( - trace_id=event["contexts"]["trace"]["trace_id"], - parent_span_id=request_span["span_id"], - sampled=1, - ) + sentry_sdk.flush() + request_span = next(item.payload for item in items) + expected_sentry_trace = "{trace_id}-{parent_span_id}-{sampled}".format( + trace_id=request_span["trace_id"], + parent_span_id=request_span["span_id"], + sampled=1, + ) - expected_outgoing_baggage = ( - "sentry-trace_id=%s," - "sentry-sample_rand=0.250000," - "sentry-environment=production," - "sentry-release=foo," - "sentry-sample_rate=0.5," - "sentry-sampled=%s" - ) % (transaction.trace_id, "true" if transaction.sampled else "false") + expected_outgoing_baggage = ( + "sentry-trace_id=%s," + "sentry-sample_rand=0.250000," + "sentry-environment=production," + "sentry-release=foo," + "sentry-transaction=Head%%20SDK%%20tx," + "sentry-sample_rate=0.5," + "sentry-sampled=true" + ) % request_span["trace_id"] assert request_headers["sentry-trace"] == expected_sentry_trace assert request_headers["baggage"] == expected_outgoing_baggage @@ -730,19 +654,17 @@ def getresponse(self, *args, **kwargs): ], ], ) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_option_trace_propagation_targets( sentry_init, trace_propagation_targets, host, path, trace_propagated, - span_streaming, ): sentry_init( trace_propagation_targets=trace_propagation_targets, traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) already_patched_getresponse = HTTPSConnection.getresponse @@ -771,31 +693,17 @@ def getresponse(self, *args, **kwargs): "sentry-public_key=49d0f7386ad645858ae85020e393bef3, sentry-sample_rate=0.01337, " ) } + sentry_sdk.traces.continue_trace(headers) - if span_streaming: - sentry_sdk.traces.continue_trace(headers) - - with sentry_sdk.traces.start_span( - name="/interactions/other-dogs/new-dog", - attributes={ - "sentry.op": "greeting.sniff", - }, - ): - connection = HTTPSConnectionRecordingRequestHeaders(host) - connection.request("GET", path) - connection.getresponse() - else: - transaction = continue_trace(headers) - - with start_transaction( - transaction=transaction, - name="/interactions/other-dogs/new-dog", - op="greeting.sniff", - trace_id="12312012123120121231201212312012", - ) as transaction: - connection = HTTPSConnectionRecordingRequestHeaders(host) - connection.request("GET", path) - connection.getresponse() + with sentry_sdk.traces.start_span( + name="/interactions/other-dogs/new-dog", + attributes={ + "sentry.op": "greeting.sniff", + }, + ): + connection = HTTPSConnectionRecordingRequestHeaders(host) + connection.request("GET", path) + connection.getresponse() if trace_propagated: assert "sentry-trace" in request_headers @@ -805,12 +713,10 @@ def getresponse(self, *args, **kwargs): assert "baggage" not in request_headers -@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_source_disabled( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_options = { "traces_sample_rate": 1.0, @@ -820,199 +726,114 @@ def test_request_source_disabled( sentry_init( **sentry_options, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) + items = capture_items("span") - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() + with sentry_sdk.traces.start_span(name="custom parent"): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/foo") + conn.getresponse() - sentry_sdk.flush() - span = next(item.payload for item in items) - assert span["name"].startswith("GET") + sentry_sdk.flush() + span = next(item.payload for item in items) + assert span["name"].startswith("GET") - attributes = span["attributes"] + attributes = span["attributes"] - assert SPANDATA.CODE_LINE_NUMBER not in attributes - assert SPANDATA.CODE_NAMESPACE not in attributes - assert SPANDATA.CODE_FILE_PATH not in attributes - assert SPANDATA.CODE_FUNCTION not in attributes - else: - events = capture_events() - - with start_transaction(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - (event,) = events - - span = event["spans"][-1] - assert span["description"].startswith("GET") - - data = span.get("data", {}) - - assert SPANDATA.CODE_LINENO not in data - assert SPANDATA.CODE_NAMESPACE not in data - assert SPANDATA.CODE_FILEPATH not in data - assert SPANDATA.CODE_FUNCTION not in data + assert SPANDATA.CODE_LINE_NUMBER not in attributes + assert SPANDATA.CODE_NAMESPACE not in attributes + assert SPANDATA.CODE_FILE_PATH not in attributes + assert SPANDATA.CODE_FUNCTION not in attributes @pytest.mark.parametrize("enable_http_request_source", [None, True]) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_source_enabled( sentry_init, capture_events, capture_items, enable_http_request_source, - span_streaming, ): sentry_options = { "traces_sample_rate": 1.0, "http_request_source_threshold_ms": 0, - "trace_lifecycle": "stream" if span_streaming else "static", + "trace_lifecycle": "stream", } if enable_http_request_source is not None: sentry_options["enable_http_request_source"] = enable_http_request_source + sentry_init( + **sentry_options, + ) - if span_streaming: - sentry_init( - **sentry_options, - ) - - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - sentry_sdk.flush() - span = next(item.payload for item in items) - assert span["name"].startswith("GET") - - attributes = span["attributes"] - - assert SPANDATA.CODE_LINE_NUMBER in attributes - assert SPANDATA.CODE_NAMESPACE in attributes - assert SPANDATA.CODE_FILE_PATH in attributes - assert SPANDATA.CODE_FUNCTION in attributes - else: - sentry_init(**sentry_options) - - events = capture_events() - - with start_transaction(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() + items = capture_items("span") - (event,) = events + with sentry_sdk.traces.start_span(name="custom parent"): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/foo") + conn.getresponse() - span = event["spans"][-1] - assert span["description"].startswith("GET") + sentry_sdk.flush() + span = next(item.payload for item in items) + assert span["name"].startswith("GET") - data = span.get("data", {}) + attributes = span["attributes"] - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data + assert SPANDATA.CODE_LINE_NUMBER in attributes + assert SPANDATA.CODE_NAMESPACE in attributes + assert SPANDATA.CODE_FILE_PATH in attributes + assert SPANDATA.CODE_FUNCTION in attributes -@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_source( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=1.0, enable_http_request_source=True, http_request_source_threshold_ms=0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") + items = capture_items("span") - with sentry_sdk.traces.start_span(name="custom parent"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - sentry_sdk.flush() - span = next(item.payload for item in items) - assert span["name"].startswith("GET") - - attributes = span["attributes"] + with sentry_sdk.traces.start_span(name="custom parent"): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/foo") + conn.getresponse() - assert SPANDATA.CODE_LINE_NUMBER in attributes - assert SPANDATA.CODE_NAMESPACE in attributes - assert SPANDATA.CODE_FILE_PATH in attributes - assert SPANDATA.CODE_FUNCTION in attributes + sentry_sdk.flush() + span = next(item.payload for item in items) + assert span["name"].startswith("GET") - assert type(attributes.get(SPANDATA.CODE_LINE_NUMBER)) == int - assert attributes.get(SPANDATA.CODE_LINE_NUMBER) > 0 - assert ( - attributes.get(SPANDATA.CODE_NAMESPACE) - == "tests.integrations.stdlib.test_httplib" - ) - assert attributes.get(SPANDATA.CODE_FILE_PATH).endswith( - "tests/integrations/stdlib/test_httplib.py" - ) + attributes = span["attributes"] - is_relative_path = attributes.get(SPANDATA.CODE_FILE_PATH)[0] != os.sep - assert is_relative_path + assert SPANDATA.CODE_LINE_NUMBER in attributes + assert SPANDATA.CODE_NAMESPACE in attributes + assert SPANDATA.CODE_FILE_PATH in attributes + assert SPANDATA.CODE_FUNCTION in attributes - assert attributes.get(SPANDATA.CODE_FUNCTION) == "test_request_source" - else: - events = capture_events() - - with start_transaction(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - (event,) = events - - span = event["spans"][-1] - assert span["description"].startswith("GET") - - data = span.get("data", {}) - - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data - - assert type(data.get(SPANDATA.CODE_LINENO)) == int - assert data.get(SPANDATA.CODE_LINENO) > 0 - assert ( - data.get(SPANDATA.CODE_NAMESPACE) - == "tests.integrations.stdlib.test_httplib" - ) - assert data.get(SPANDATA.CODE_FILEPATH).endswith( - "tests/integrations/stdlib/test_httplib.py" - ) + assert type(attributes.get(SPANDATA.CODE_LINE_NUMBER)) == int + assert attributes.get(SPANDATA.CODE_LINE_NUMBER) > 0 + assert ( + attributes.get(SPANDATA.CODE_NAMESPACE) + == "tests.integrations.stdlib.test_httplib" + ) + assert attributes.get(SPANDATA.CODE_FILE_PATH).endswith( + "tests/integrations/stdlib/test_httplib.py" + ) - is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep - assert is_relative_path + is_relative_path = attributes.get(SPANDATA.CODE_FILE_PATH)[0] != os.sep + assert is_relative_path - assert data.get(SPANDATA.CODE_FUNCTION) == "test_request_source" + assert attributes.get(SPANDATA.CODE_FUNCTION) == "test_request_source" -@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_source_with_module_in_search_path( sentry_init, capture_events, capture_items, - span_streaming, ): """ Test that request source is relative to the path of the module it ran in @@ -1021,462 +842,270 @@ def test_request_source_with_module_in_search_path( traces_sample_rate=1.0, enable_http_request_source=True, http_request_source_threshold_ms=0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - from httplib_helpers.helpers import get_request_with_connection - - conn = HTTPConnection("localhost", port=PORT) - get_request_with_connection(conn, "/foo") - - sentry_sdk.flush() - span = next(item.payload for item in items) - assert span["name"].startswith("GET") - - attributes = span["attributes"] - - assert SPANDATA.CODE_LINE_NUMBER in attributes - assert SPANDATA.CODE_NAMESPACE in attributes - assert SPANDATA.CODE_FILE_PATH in attributes - assert SPANDATA.CODE_FUNCTION in attributes - - assert type(attributes.get(SPANDATA.CODE_LINE_NUMBER)) == int - assert attributes.get(SPANDATA.CODE_LINE_NUMBER) > 0 - assert attributes.get(SPANDATA.CODE_NAMESPACE) == "httplib_helpers.helpers" - assert attributes.get(SPANDATA.CODE_FILE_PATH) == "httplib_helpers/helpers.py" - - is_relative_path = attributes.get(SPANDATA.CODE_FILE_PATH)[0] != os.sep - assert is_relative_path - - assert attributes.get(SPANDATA.CODE_FUNCTION) == "get_request_with_connection" - else: - events = capture_events() + items = capture_items("span") - with start_transaction(name="foo"): - from httplib_helpers.helpers import get_request_with_connection + with sentry_sdk.traces.start_span(name="custom parent"): + from httplib_helpers.helpers import get_request_with_connection - conn = HTTPConnection("localhost", port=PORT) - get_request_with_connection(conn, "/foo") - - (event,) = events + conn = HTTPConnection("localhost", port=PORT) + get_request_with_connection(conn, "/foo") - span = event["spans"][-1] - assert span["description"].startswith("GET") + sentry_sdk.flush() + span = next(item.payload for item in items) + assert span["name"].startswith("GET") - data = span.get("data", {}) + attributes = span["attributes"] - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data + assert SPANDATA.CODE_LINE_NUMBER in attributes + assert SPANDATA.CODE_NAMESPACE in attributes + assert SPANDATA.CODE_FILE_PATH in attributes + assert SPANDATA.CODE_FUNCTION in attributes - assert type(data.get(SPANDATA.CODE_LINENO)) == int - assert data.get(SPANDATA.CODE_LINENO) > 0 - assert data.get(SPANDATA.CODE_NAMESPACE) == "httplib_helpers.helpers" - assert data.get(SPANDATA.CODE_FILEPATH) == "httplib_helpers/helpers.py" + assert type(attributes.get(SPANDATA.CODE_LINE_NUMBER)) == int + assert attributes.get(SPANDATA.CODE_LINE_NUMBER) > 0 + assert attributes.get(SPANDATA.CODE_NAMESPACE) == "httplib_helpers.helpers" + assert attributes.get(SPANDATA.CODE_FILE_PATH) == "httplib_helpers/helpers.py" - is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep - assert is_relative_path + is_relative_path = attributes.get(SPANDATA.CODE_FILE_PATH)[0] != os.sep + assert is_relative_path - assert data.get(SPANDATA.CODE_FUNCTION) == "get_request_with_connection" + assert attributes.get(SPANDATA.CODE_FUNCTION) == "get_request_with_connection" -@pytest.mark.parametrize("span_streaming", [True, False]) def test_no_request_source_if_duration_too_short( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=1.0, enable_http_request_source=True, http_request_source_threshold_ms=100, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) add_http_request_source = sentry_sdk.tracing_utils.add_http_request_source def add_http_request_source_with_pinned_timestamps(span): - if span_streaming: - span._start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span._end_timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) - result = add_http_request_source(span) - span._end_timestamp = None - return result - else: - span.start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span.timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) - return add_http_request_source(span) - - if span_streaming: - items = capture_items("span") - with mock.patch( - "sentry_sdk.integrations.stdlib.add_http_request_source", - add_http_request_source_with_pinned_timestamps, - ): - with sentry_sdk.traces.start_span(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - sentry_sdk.flush() - span = next(item.payload for item in items) - assert span["name"].startswith("GET") - - attributes = span["attributes"] - - assert SPANDATA.CODE_LINE_NUMBER not in attributes - assert SPANDATA.CODE_NAMESPACE not in attributes - assert SPANDATA.CODE_FILE_PATH not in attributes - assert SPANDATA.CODE_FUNCTION not in attributes - else: - events = capture_events() - - with mock.patch( - "sentry_sdk.integrations.stdlib.add_http_request_source", - add_http_request_source_with_pinned_timestamps, - ): - with start_transaction(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - (event,) = events + span._start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) + span._end_timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) + result = add_http_request_source(span) + span._end_timestamp = None + return result + + items = capture_items("span") + with mock.patch( + "sentry_sdk.integrations.stdlib.add_http_request_source", + add_http_request_source_with_pinned_timestamps, + ): + with sentry_sdk.traces.start_span(name="foo"): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/foo") + conn.getresponse() - span = event["spans"][-1] - assert span["description"].startswith("GET") + sentry_sdk.flush() + span = next(item.payload for item in items) + assert span["name"].startswith("GET") - data = span.get("data", {}) + attributes = span["attributes"] - assert SPANDATA.CODE_LINENO not in data - assert SPANDATA.CODE_NAMESPACE not in data - assert SPANDATA.CODE_FILEPATH not in data - assert SPANDATA.CODE_FUNCTION not in data + assert SPANDATA.CODE_LINE_NUMBER not in attributes + assert SPANDATA.CODE_NAMESPACE not in attributes + assert SPANDATA.CODE_FILE_PATH not in attributes + assert SPANDATA.CODE_FUNCTION not in attributes -@pytest.mark.parametrize("span_streaming", [True, False]) def test_request_source_if_duration_over_threshold( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=1.0, enable_http_request_source=True, http_request_source_threshold_ms=100, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) add_http_request_source = sentry_sdk.tracing_utils.add_http_request_source def add_http_request_source_with_pinned_timestamps(span): - if span_streaming: - span._start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span._end_timestamp = datetime.datetime(2024, 1, 1, microsecond=100001) - result = add_http_request_source(span) - span._end_timestamp = None - return result - else: - span.start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span.timestamp = datetime.datetime(2024, 1, 1, microsecond=100001) - return add_http_request_source(span) - - if span_streaming: - items = capture_items("span") - - with mock.patch( - "sentry_sdk.integrations.stdlib.add_http_request_source", - add_http_request_source_with_pinned_timestamps, - ): - with sentry_sdk.traces.start_span(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - sentry_sdk.flush() - span = next(item.payload for item in items) - assert span["name"].startswith("GET") - - attributes = span["attributes"] - - assert SPANDATA.CODE_LINE_NUMBER in attributes - assert SPANDATA.CODE_NAMESPACE in attributes - assert SPANDATA.CODE_FILE_PATH in attributes - assert SPANDATA.CODE_FUNCTION in attributes - - assert type(attributes.get(SPANDATA.CODE_LINE_NUMBER)) == int - assert attributes.get(SPANDATA.CODE_LINE_NUMBER) > 0 - assert ( - attributes.get(SPANDATA.CODE_NAMESPACE) - == "tests.integrations.stdlib.test_httplib" - ) - assert attributes.get(SPANDATA.CODE_FILE_PATH).endswith( - "tests/integrations/stdlib/test_httplib.py" - ) - - is_relative_path = attributes.get(SPANDATA.CODE_FILE_PATH)[0] != os.sep - assert is_relative_path - - assert ( - attributes.get(SPANDATA.CODE_FUNCTION) - == "add_http_request_source_with_pinned_timestamps" - ) - else: - events = capture_events() - - with mock.patch( - "sentry_sdk.integrations.stdlib.add_http_request_source", - add_http_request_source_with_pinned_timestamps, - ): - with start_transaction(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - (event,) = events + span._start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) + span._end_timestamp = datetime.datetime(2024, 1, 1, microsecond=100001) + result = add_http_request_source(span) + span._end_timestamp = None + return result + + items = capture_items("span") + + with mock.patch( + "sentry_sdk.integrations.stdlib.add_http_request_source", + add_http_request_source_with_pinned_timestamps, + ): + with sentry_sdk.traces.start_span(name="foo"): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/foo") + conn.getresponse() - span = event["spans"][-1] - assert span["description"].startswith("GET") + sentry_sdk.flush() + span = next(item.payload for item in items) + assert span["name"].startswith("GET") - data = span.get("data", {}) + attributes = span["attributes"] - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data + assert SPANDATA.CODE_LINE_NUMBER in attributes + assert SPANDATA.CODE_NAMESPACE in attributes + assert SPANDATA.CODE_FILE_PATH in attributes + assert SPANDATA.CODE_FUNCTION in attributes - assert type(data.get(SPANDATA.CODE_LINENO)) == int - assert data.get(SPANDATA.CODE_LINENO) > 0 - assert ( - data.get(SPANDATA.CODE_NAMESPACE) - == "tests.integrations.stdlib.test_httplib" - ) - assert data.get(SPANDATA.CODE_FILEPATH).endswith( - "tests/integrations/stdlib/test_httplib.py" - ) + assert type(attributes.get(SPANDATA.CODE_LINE_NUMBER)) == int + assert attributes.get(SPANDATA.CODE_LINE_NUMBER) > 0 + assert ( + attributes.get(SPANDATA.CODE_NAMESPACE) + == "tests.integrations.stdlib.test_httplib" + ) + assert attributes.get(SPANDATA.CODE_FILE_PATH).endswith( + "tests/integrations/stdlib/test_httplib.py" + ) - is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep - assert is_relative_path + is_relative_path = attributes.get(SPANDATA.CODE_FILE_PATH)[0] != os.sep + assert is_relative_path - assert ( - data.get(SPANDATA.CODE_FUNCTION) - == "add_http_request_source_with_pinned_timestamps" - ) + assert ( + attributes.get(SPANDATA.CODE_FUNCTION) + == "add_http_request_source_with_pinned_timestamps" + ) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_span_origin( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=1.0, debug=True, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) + items = capture_items("span") - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() - - sentry_sdk.flush() - spans = [item.payload for item in items] - assert spans[1]["attributes"]["sentry.origin"] == "manual" - - assert spans[0]["attributes"]["sentry.op"] == "http.client" - assert spans[0]["attributes"]["sentry.origin"] == "auto.http.stdlib.httplib" - else: - events = capture_events() - - with start_transaction(name="foo"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/foo") - conn.getresponse() + with sentry_sdk.traces.start_span(name="custom parent"): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/foo") + conn.getresponse() - (event,) = events - assert event["contexts"]["trace"]["origin"] == "manual" + sentry_sdk.flush() + spans = [item.payload for item in items] + assert spans[1]["attributes"]["sentry.origin"] == "manual" - assert event["spans"][0]["op"] == "http.client" - assert event["spans"][0]["origin"] == "auto.http.stdlib.httplib" + assert spans[0]["attributes"]["sentry.op"] == "http.client" + assert spans[0]["attributes"]["sentry.origin"] == "auto.http.stdlib.httplib" -@pytest.mark.parametrize("span_streaming", [True, False]) def test_http_timeout( monkeypatch, sentry_init, capture_envelopes, capture_items, - span_streaming, ): mock_readinto = mock.Mock(side_effect=TimeoutError) monkeypatch.setattr(SocketIO, "readinto", mock_readinto) sentry_init( traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) + items = capture_items("span") - if span_streaming: - items = capture_items("span") - - with pytest.raises(TimeoutError): - with sentry_sdk.traces.start_span( - name="name", - attributes={ - "sentry.op": "op", - }, - ): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/bla") - conn.getresponse() - - sentry_sdk.flush() - spans = [item.payload for item in items] - assert len(spans) == 2 - span = spans[0] - assert span["attributes"]["sentry.op"] == "http.client" - assert span["name"] == f"GET http://localhost:{PORT}/bla" # noqa: E231 - else: - envelopes = capture_envelopes() - - with pytest.raises(TimeoutError): - with start_transaction(op="op", name="name"): - conn = HTTPConnection("localhost", port=PORT) - conn.request("GET", "/bla") - conn.getresponse() - - (transaction_envelope,) = envelopes - transaction = transaction_envelope.get_transaction_event() - assert len(transaction["spans"]) == 1 + with pytest.raises(TimeoutError): + with sentry_sdk.traces.start_span( + name="name", + attributes={ + "sentry.op": "op", + }, + ): + conn = HTTPConnection("localhost", port=PORT) + conn.request("GET", "/bla") + conn.getresponse() - span = transaction["spans"][0] - assert span["op"] == "http.client" - assert span["description"] == f"GET http://localhost:{PORT}/bla" # noqa: E231 + sentry_sdk.flush() + spans = [item.payload for item in items] + assert len(spans) == 2 + span = spans[0] + assert span["attributes"]["sentry.op"] == "http.client" + assert span["name"] == f"GET http://localhost:{PORT}/bla" # noqa: E231 @pytest.mark.parametrize("tunnel_port", [8080, None]) -@pytest.mark.parametrize("span_streaming", [True, False]) @pytest.mark.parametrize("send_default_pii", [True, False]) def test_proxy_http_tunnel( sentry_init, capture_events, capture_items, tunnel_port, - span_streaming, send_default_pii, ): sentry_init( traces_sample_rate=1.0, send_default_pii=send_default_pii, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", + ) + items = capture_items("span") + + with sentry_sdk.traces.start_span(name="custom parent"): + conn = HTTPConnection("localhost", PROXY_PORT) + conn.set_tunnel("api.example.com", tunnel_port) + conn.request("GET", "/foo?bar=1") + conn.getresponse() + + sentry_sdk.flush() + spans = [item.payload for item in items] + (span,) = ( + span + for span in spans + if span["attributes"].get("sentry.origin") == "auto.http.stdlib.httplib" ) - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - conn = HTTPConnection("localhost", PROXY_PORT) - conn.set_tunnel("api.example.com", tunnel_port) - conn.request("GET", "/foo?bar=1") - conn.getresponse() + port_modifier = f":{tunnel_port}" if tunnel_port else "" + assert span["name"] == f"GET http://api.example.com{port_modifier}/foo" + assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET" + assert span["attributes"][SPANDATA.NETWORK_PEER_ADDRESS] == "localhost" + assert span["attributes"][SPANDATA.NETWORK_PEER_PORT] == PROXY_PORT - sentry_sdk.flush() - spans = [item.payload for item in items] - (span,) = ( - span - for span in spans - if span["attributes"].get("sentry.origin") == "auto.http.stdlib.httplib" + if send_default_pii: + assert ( + span["attributes"][SPANDATA.URL_FULL] + == f"http://api.example.com{port_modifier}/foo" ) - - port_modifier = f":{tunnel_port}" if tunnel_port else "" - assert span["name"] == f"GET http://api.example.com{port_modifier}/foo" - assert span["attributes"][SPANDATA.HTTP_REQUEST_METHOD] == "GET" - assert span["attributes"][SPANDATA.NETWORK_PEER_ADDRESS] == "localhost" - assert span["attributes"][SPANDATA.NETWORK_PEER_PORT] == PROXY_PORT - - if send_default_pii: - assert ( - span["attributes"][SPANDATA.URL_FULL] - == f"http://api.example.com{port_modifier}/foo" - ) - assert span["attributes"][SPANDATA.URL_QUERY] == "bar=1" - else: - assert SPANDATA.URL_FULL not in span["attributes"] - assert SPANDATA.URL_QUERY not in span["attributes"] + assert span["attributes"][SPANDATA.URL_QUERY] == "bar=1" else: - events = capture_events() + assert SPANDATA.URL_FULL not in span["attributes"] + assert SPANDATA.URL_QUERY not in span["attributes"] - with start_transaction(name="test_transaction"): - conn = HTTPConnection("localhost", PROXY_PORT) - conn.set_tunnel("api.example.com", tunnel_port) - conn.request("GET", "/foo?bar=1") - conn.getresponse() - (event,) = events - (span,) = event["spans"] - - port_modifier = f":{tunnel_port}" if tunnel_port else "" - assert span["description"] == f"GET http://api.example.com{port_modifier}/foo" - assert span["data"][SPANDATA.HTTP_METHOD] == "GET" - assert span["data"][SPANDATA.NETWORK_PEER_ADDRESS] == "localhost" - assert span["data"][SPANDATA.NETWORK_PEER_PORT] == PROXY_PORT - assert span["data"]["url"] == f"http://api.example.com{port_modifier}/foo" - assert span["data"][SPANDATA.HTTP_QUERY] == "bar=1" - - -@pytest.mark.parametrize("span_streaming", [True, False]) def test_chunked_response_span_covers_body_read( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) min_expected_duration = CHUNK_DELAY * NUM_CHUNKS + items = capture_items("span") - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - conn = HTTPConnection("localhost", CHUNKED_PORT) - conn.request("GET", "/chunked") - response = conn.getresponse() - response.read() + with sentry_sdk.traces.start_span(name="custom parent"): + conn = HTTPConnection("localhost", CHUNKED_PORT) + conn.request("GET", "/chunked") + response = conn.getresponse() + response.read() - sentry_sdk.flush() - http_span, parent_span = [item.payload for item in items] + sentry_sdk.flush() + http_span, parent_span = [item.payload for item in items] - duration = http_span["end_timestamp"] - http_span["start_timestamp"] - assert duration >= min_expected_duration - else: - events = capture_events() - - with start_transaction(name="test_chunked"): - conn = HTTPConnection("localhost", CHUNKED_PORT) - conn.request("GET", "/chunked") - response = conn.getresponse() - response.read() - - (event,) = events - (span,) = event["spans"] - - fmt = "%Y-%m-%dT%H:%M:%S.%fZ" - start = datetime.datetime.strptime(span["start_timestamp"], fmt) - end = datetime.datetime.strptime(span["timestamp"], fmt) - duration = (end - start).total_seconds() - assert duration >= min_expected_duration + duration = http_span["end_timestamp"] - http_span["start_timestamp"] + assert duration >= min_expected_duration diff --git a/tests/integrations/stdlib/test_subprocess.py b/tests/integrations/stdlib/test_subprocess.py index e67ef22e2b..d2310d2379 100644 --- a/tests/integrations/stdlib/test_subprocess.py +++ b/tests/integrations/stdlib/test_subprocess.py @@ -7,7 +7,7 @@ import pytest import sentry_sdk -from sentry_sdk import capture_message, start_transaction +from sentry_sdk import capture_message from sentry_sdk.consts import SPANDATA from sentry_sdk.integrations.stdlib import StdlibIntegration from tests.conftest import ApproxDict @@ -44,7 +44,6 @@ def __len__(self): ) @pytest.mark.parametrize("env_mapping", [None, os.environ, ImmutableDict(os.environ)]) @pytest.mark.parametrize("with_cwd", [True, False]) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_subprocess_basic( sentry_init, capture_events, @@ -54,7 +53,6 @@ def test_subprocess_basic( iterator, env_mapping, with_cwd, - span_streaming, ): monkeypatch.setenv("FOO", "bar") @@ -63,271 +61,137 @@ def test_subprocess_basic( sentry_init( integrations=[StdlibIntegration()], traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) + items = capture_items("event", "span") + + with sentry_sdk.traces.start_span(name="custom parent") as span: + args = [ + sys.executable, + "-c", + "import os; " + "import sentry_sdk; " + "from sentry_sdk.integrations.stdlib import get_subprocess_traceparent_headers; " + "sentry_sdk.init(); " + "assert os.environ['FOO'] == 'bar'; " + "print(dict(get_subprocess_traceparent_headers()))", + ] - if span_streaming: - items = capture_items("event", "span") - - with sentry_sdk.traces.start_span(name="custom parent") as span: - args = [ - sys.executable, - "-c", - "import os; " - "import sentry_sdk; " - "from sentry_sdk.integrations.stdlib import get_subprocess_traceparent_headers; " - "sentry_sdk.init(); " - "assert os.environ['FOO'] == 'bar'; " - "print(dict(get_subprocess_traceparent_headers()))", - ] - - if iterator: - args = iter(args) - - if positional_args: - a = ( - args, - 0, # bufsize - None, # executable - None, # stdin - subprocess.PIPE, # stdout - None, # stderr - None, # preexec_fn - False, # close_fds - False, # shell - os.getcwd() if with_cwd else None, # cwd - ) - - if env_mapping is not None: - a += (env_mapping,) - - popen = subprocess.Popen(*a) - - else: - kw = {"args": args, "stdout": subprocess.PIPE} - - if with_cwd: - kw["cwd"] = os.getcwd() - - if env_mapping is not None: - kw["env"] = env_mapping - - popen = subprocess.Popen(**kw) - - output, unused_err = popen.communicate() - retcode = popen.poll() - assert not retcode - - assert os.environ == old_environ - - assert span.trace_id in str(output) - - capture_message("hi") - - (message_event,) = (item.payload for item in items if item.type == "event") - assert message_event["message"] == "hi" - - data = ApproxDict({}) - - sentry_sdk.flush() - ( - subprocess_init_span, - subprocess_wait_span, - subprocess_communicate_span, - parent_span, - ) = (item.payload for item in items if item.type == "span") - - assert ( - subprocess_init_span["attributes"]["sentry.op"], - subprocess_wait_span["attributes"]["sentry.op"], - subprocess_communicate_span["attributes"]["sentry.op"], - ) == ("subprocess", "subprocess.wait", "subprocess.communicate") - - # span hierarchy - assert ( - subprocess_wait_span["parent_span_id"] - == subprocess_communicate_span["span_id"] - ) - - assert ( - subprocess_communicate_span["parent_span_id"] - == subprocess_init_span["parent_span_id"] - == parent_span["span_id"] - ) - - assert ( - subprocess_init_span["attributes"][SPANDATA.PROCESS_PID] - == subprocess_wait_span["attributes"][SPANDATA.PROCESS_PID] - == subprocess_communicate_span["attributes"][SPANDATA.PROCESS_PID] - ) - - # data of init span - assert subprocess_init_span.get("attributes", {}) == data if iterator: - assert "iterator" in subprocess_init_span["name"] - assert subprocess_init_span["name"].startswith("<") + args = iter(args) + + if positional_args: + a = ( + args, + 0, # bufsize + None, # executable + None, # stdin + subprocess.PIPE, # stdout + None, # stderr + None, # preexec_fn + False, # close_fds + False, # shell + os.getcwd() if with_cwd else None, # cwd + ) + + if env_mapping is not None: + a += (env_mapping,) + + popen = subprocess.Popen(*a) + else: - assert sys.executable + " -c" in subprocess_init_span["name"] + kw = {"args": args, "stdout": subprocess.PIPE} + + if with_cwd: + kw["cwd"] = os.getcwd() + + if env_mapping is not None: + kw["env"] = env_mapping + + popen = subprocess.Popen(**kw) + + output, unused_err = popen.communicate() + retcode = popen.poll() + assert not retcode + + assert os.environ == old_environ + + assert span.trace_id in str(output) + capture_message("hi") + + (message_event,) = (item.payload for item in items if item.type == "event") + assert message_event["message"] == "hi" + + data = ApproxDict({}) + + sentry_sdk.flush() + ( + subprocess_init_span, + subprocess_wait_span, + subprocess_communicate_span, + parent_span, + ) = (item.payload for item in items if item.type == "span") + + assert ( + subprocess_init_span["attributes"]["sentry.op"], + subprocess_wait_span["attributes"]["sentry.op"], + subprocess_communicate_span["attributes"]["sentry.op"], + ) == ("subprocess", "subprocess.wait", "subprocess.communicate") + + # span hierarchy + assert ( + subprocess_wait_span["parent_span_id"] == subprocess_communicate_span["span_id"] + ) + + assert ( + subprocess_communicate_span["parent_span_id"] + == subprocess_init_span["parent_span_id"] + == parent_span["span_id"] + ) + + assert ( + subprocess_init_span["attributes"][SPANDATA.PROCESS_PID] + == subprocess_wait_span["attributes"][SPANDATA.PROCESS_PID] + == subprocess_communicate_span["attributes"][SPANDATA.PROCESS_PID] + ) + + # data of init span + assert subprocess_init_span.get("attributes", {}) == data + if iterator: + assert "iterator" in subprocess_init_span["name"] + assert subprocess_init_span["name"].startswith("<") else: - events = capture_events() - - with start_transaction(name="foo") as transaction: - args = [ - sys.executable, - "-c", - "import os; " - "import sentry_sdk; " - "from sentry_sdk.integrations.stdlib import get_subprocess_traceparent_headers; " - "sentry_sdk.init(); " - "assert os.environ['FOO'] == 'bar'; " - "print(dict(get_subprocess_traceparent_headers()))", - ] - - if iterator: - args = iter(args) - - if positional_args: - a = ( - args, - 0, # bufsize - None, # executable - None, # stdin - subprocess.PIPE, # stdout - None, # stderr - None, # preexec_fn - False, # close_fds - False, # shell - os.getcwd() if with_cwd else None, # cwd - ) - - if env_mapping is not None: - a += (env_mapping,) - - popen = subprocess.Popen(*a) - - else: - kw = {"args": args, "stdout": subprocess.PIPE} - - if with_cwd: - kw["cwd"] = os.getcwd() - - if env_mapping is not None: - kw["env"] = env_mapping - - popen = subprocess.Popen(**kw) - - output, unused_err = popen.communicate() - retcode = popen.poll() - assert not retcode - - assert os.environ == old_environ - - assert transaction.trace_id in str(output) - - capture_message("hi") - - ( - transaction_event, - message_event, - ) = events - - assert message_event["message"] == "hi" - - data = ApproxDict({"subprocess.cwd": os.getcwd()} if with_cwd else {}) - - (crumb,) = message_event["breadcrumbs"]["values"] - assert crumb == { - "category": "subprocess", - "data": data, - "message": crumb["message"], - "timestamp": crumb["timestamp"], - "type": "subprocess", - } - - if not iterator: - assert crumb["message"].startswith(sys.executable + " ") - - assert transaction_event["type"] == "transaction" - - ( - subprocess_init_span, - subprocess_communicate_span, - subprocess_wait_span, - ) = transaction_event["spans"] - - assert ( - subprocess_init_span["op"], - subprocess_communicate_span["op"], - subprocess_wait_span["op"], - ) == ("subprocess", "subprocess.communicate", "subprocess.wait") - - assert ( - subprocess_wait_span["parent_span_id"] - == subprocess_communicate_span["span_id"] - ) - - assert ( - subprocess_communicate_span["parent_span_id"] - == subprocess_init_span["parent_span_id"] - == transaction_event["contexts"]["trace"]["span_id"] - ) - - assert ( - subprocess_init_span["tags"]["subprocess.pid"] - == subprocess_wait_span["tags"]["subprocess.pid"] - == subprocess_communicate_span["tags"]["subprocess.pid"] - ) - - # data of init span - assert subprocess_init_span.get("data", {}) == data - if iterator: - assert "iterator" in subprocess_init_span["description"] - assert subprocess_init_span["description"].startswith("<") - else: - assert sys.executable + " -c" in subprocess_init_span["description"] + assert sys.executable + " -c" in subprocess_init_span["name"] -@pytest.mark.parametrize("span_streaming", [True, False]) def test_subprocess_empty_env( sentry_init, monkeypatch, - span_streaming, ): monkeypatch.setenv("TEST_MARKER", "should_not_be_seen") sentry_init( integrations=[StdlibIntegration()], traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - with sentry_sdk.traces.start_span(name="custom parent"): - args = [ - sys.executable, - "-c", - "import os; print(os.environ.get('TEST_MARKER', None))", - ] - output = subprocess.check_output(args, env={}, universal_newlines=True) - else: - with start_transaction(name="foo"): - args = [ - sys.executable, - "-c", - "import os; print(os.environ.get('TEST_MARKER', None))", - ] - output = subprocess.check_output(args, env={}, universal_newlines=True) + with sentry_sdk.traces.start_span(name="custom parent"): + args = [ + sys.executable, + "-c", + "import os; print(os.environ.get('TEST_MARKER', None))", + ] + output = subprocess.check_output(args, env={}, universal_newlines=True) assert "should_not_be_seen" not in output -@pytest.mark.parametrize("span_streaming", [True, False]) def test_subprocess_invalid_args( sentry_init, - span_streaming, ): sentry_init( integrations=[StdlibIntegration()], - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) with pytest.raises(TypeError) as excinfo: @@ -336,80 +200,46 @@ def test_subprocess_invalid_args( assert "'int' object is not iterable" in str(excinfo.value) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_subprocess_span_origin( sentry_init, capture_events, capture_items, - span_streaming, ): sentry_init( integrations=[StdlibIntegration()], traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) + items = capture_items("span") - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="custom parent"): - args = [ - sys.executable, - "-c", - "print('hello world')", - ] - kw = {"args": args, "stdout": subprocess.PIPE} - - popen = subprocess.Popen(**kw) - popen.communicate() - popen.poll() - - sentry_sdk.flush() - spans = [item.payload for item in items] - - assert spans[3]["attributes"]["sentry.origin"] == "manual" - - assert spans[0]["attributes"]["sentry.op"] == "subprocess" - assert ( - spans[0]["attributes"]["sentry.origin"] - == "auto.subprocess.stdlib.subprocess" - ) - - assert spans[1]["attributes"]["sentry.op"] == "subprocess.wait" - assert ( - spans[1]["attributes"]["sentry.origin"] - == "auto.subprocess.stdlib.subprocess" - ) - - assert spans[2]["attributes"]["sentry.op"] == "subprocess.communicate" - assert ( - spans[2]["attributes"]["sentry.origin"] - == "auto.subprocess.stdlib.subprocess" - ) - else: - events = capture_events() - - with start_transaction(name="foo"): - args = [ - sys.executable, - "-c", - "print('hello world')", - ] - kw = {"args": args, "stdout": subprocess.PIPE} + with sentry_sdk.traces.start_span(name="custom parent"): + args = [ + sys.executable, + "-c", + "print('hello world')", + ] + kw = {"args": args, "stdout": subprocess.PIPE} - popen = subprocess.Popen(**kw) - popen.communicate() - popen.poll() + popen = subprocess.Popen(**kw) + popen.communicate() + popen.poll() - (event,) = events + sentry_sdk.flush() + spans = [item.payload for item in items] - assert event["contexts"]["trace"]["origin"] == "manual" + assert spans[3]["attributes"]["sentry.origin"] == "manual" - assert event["spans"][0]["op"] == "subprocess" - assert event["spans"][0]["origin"] == "auto.subprocess.stdlib.subprocess" + assert spans[0]["attributes"]["sentry.op"] == "subprocess" + assert ( + spans[0]["attributes"]["sentry.origin"] == "auto.subprocess.stdlib.subprocess" + ) - assert event["spans"][1]["op"] == "subprocess.communicate" - assert event["spans"][1]["origin"] == "auto.subprocess.stdlib.subprocess" + assert spans[1]["attributes"]["sentry.op"] == "subprocess.wait" + assert ( + spans[1]["attributes"]["sentry.origin"] == "auto.subprocess.stdlib.subprocess" + ) - assert event["spans"][2]["op"] == "subprocess.wait" - assert event["spans"][2]["origin"] == "auto.subprocess.stdlib.subprocess" + assert spans[2]["attributes"]["sentry.op"] == "subprocess.communicate" + assert ( + spans[2]["attributes"]["sentry.origin"] == "auto.subprocess.stdlib.subprocess" + ) From 95363d373f8f58c92a44206c588ff95a274dc0fe Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 09:37:25 +0200 Subject: [PATCH 2/7] remove unused parameters and fix breadcrumb tests --- tests/integrations/stdlib/test_httplib.py | 43 ++++++++------------ tests/integrations/stdlib/test_subprocess.py | 2 - 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index 98a4bf10a9..661b75d3c6 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -92,12 +92,12 @@ def test_crumb_capture(sentry_init, capture_events): assert crumb["category"] == "httplib" assert crumb["data"] == ApproxDict( { - "url": url, - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.URL_FULL: url, + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, "reason": "OK", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", + SPANDATA.URL_FRAGMENT: "", + SPANDATA.URL_QUERY: "", } ) @@ -176,11 +176,11 @@ def test_crumb_capture_client_error(sentry_init, capture_events, status_code, le assert crumb["data"] == ApproxDict( { - "url": url, - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.URL_FULL: url, + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: status_code, - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", + SPANDATA.URL_FRAGMENT: "", + SPANDATA.URL_QUERY: "", } ) @@ -267,13 +267,13 @@ def before_breadcrumb(crumb, hint): assert crumb["category"] == "httplib" assert crumb["data"] == ApproxDict( { - "url": url, - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.URL_FULL: url, + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, "reason": "OK", "extra": "foo", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", + SPANDATA.URL_FRAGMENT: "", + SPANDATA.URL_QUERY: "", } ) @@ -379,19 +379,18 @@ def test_httplib_misuse(sentry_init, capture_events, request): assert crumb["category"] == "httplib" assert crumb["data"] == ApproxDict( { - "url": "http://localhost:{}/200".format(PORT), - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.URL_FULL: "http://localhost:{}/200".format(PORT), + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, "reason": "OK", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", + SPANDATA.URL_FRAGMENT: "", + SPANDATA.URL_QUERY: "", } ) def test_outgoing_trace_headers( sentry_init, - capture_events, capture_items, ): sentry_init( @@ -467,7 +466,6 @@ def getresponse(self, *args, **kwargs): def test_outgoing_trace_headers_head_sdk( sentry_init, - capture_events, capture_items, ): sentry_init( @@ -715,7 +713,6 @@ def getresponse(self, *args, **kwargs): def test_request_source_disabled( sentry_init, - capture_events, capture_items, ): sentry_options = { @@ -750,7 +747,6 @@ def test_request_source_disabled( @pytest.mark.parametrize("enable_http_request_source", [None, True]) def test_request_source_enabled( sentry_init, - capture_events, capture_items, enable_http_request_source, ): @@ -787,7 +783,6 @@ def test_request_source_enabled( def test_request_source( sentry_init, - capture_events, capture_items, ): sentry_init( @@ -832,7 +827,6 @@ def test_request_source( def test_request_source_with_module_in_search_path( sentry_init, - capture_events, capture_items, ): """ @@ -876,7 +870,6 @@ def test_request_source_with_module_in_search_path( def test_no_request_source_if_duration_too_short( sentry_init, - capture_events, capture_items, ): sentry_init( @@ -919,7 +912,6 @@ def add_http_request_source_with_pinned_timestamps(span): def test_request_source_if_duration_over_threshold( sentry_init, - capture_events, capture_items, ): sentry_init( @@ -981,7 +973,6 @@ def add_http_request_source_with_pinned_timestamps(span): def test_span_origin( sentry_init, - capture_events, capture_items, ): sentry_init( @@ -1042,7 +1033,6 @@ def test_http_timeout( @pytest.mark.parametrize("send_default_pii", [True, False]) def test_proxy_http_tunnel( sentry_init, - capture_events, capture_items, tunnel_port, send_default_pii, @@ -1087,7 +1077,6 @@ def test_proxy_http_tunnel( def test_chunked_response_span_covers_body_read( sentry_init, - capture_events, capture_items, ): sentry_init( diff --git a/tests/integrations/stdlib/test_subprocess.py b/tests/integrations/stdlib/test_subprocess.py index d2310d2379..1fd2322f8e 100644 --- a/tests/integrations/stdlib/test_subprocess.py +++ b/tests/integrations/stdlib/test_subprocess.py @@ -46,7 +46,6 @@ def __len__(self): @pytest.mark.parametrize("with_cwd", [True, False]) def test_subprocess_basic( sentry_init, - capture_events, capture_items, monkeypatch, positional_args, @@ -202,7 +201,6 @@ def test_subprocess_invalid_args( def test_subprocess_span_origin( sentry_init, - capture_events, capture_items, ): sentry_init( From 6561b184ed81493cb62185f02db446f13b902b98 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 09:40:38 +0200 Subject: [PATCH 3/7] fix breadcrumb assertions in requests test --- tests/integrations/requests/test_requests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integrations/requests/test_requests.py b/tests/integrations/requests/test_requests.py index eb80f62b8a..e0db8133ad 100644 --- a/tests/integrations/requests/test_requests.py +++ b/tests/integrations/requests/test_requests.py @@ -26,9 +26,9 @@ def test_crumb_capture(sentry_init, capture_events): assert crumb["data"] == ApproxDict( { "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", + SPANDATA.HTTP_REQUEST_METHOD: "GET", + SPANDATA.URL_FRAGMENT: "", + SPANDATA.URL_QUERY: "", SPANDATA.HTTP_STATUS_CODE: response.status_code, "reason": response.reason, } From b95bf72b04b64c01950ef573306d4ed7ebb89fd4 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 09:49:04 +0200 Subject: [PATCH 4/7] remove legacy breadcrumb tests --- tests/integrations/httpx/test_httpx.py | 185 +------------------ tests/integrations/httpx2/test_httpx2.py | 185 +------------------ tests/integrations/requests/test_requests.py | 72 +------- tests/integrations/stdlib/test_httplib.py | 112 +---------- 4 files changed, 13 insertions(+), 541 deletions(-) diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index d62f5da4d0..838ffcdb84 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -11,48 +11,8 @@ from tests.conftest import ApproxDict -def test_crumb_capture_and_hint_sync(sentry_init, capture_events, httpx_mock): - httpx_mock.add_response() - - def before_breadcrumb(crumb, hint): - crumb["data"]["extra"] = "foo" - return crumb - - sentry_init( - integrations=[HttpxIntegration()], - before_breadcrumb=before_breadcrumb, - ) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = httpx.Client().get(url) - - assert response.status_code == 200 - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", - "extra": "foo", - } - ) - - @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_and_hint_sync_span_streaming( +def test_crumb_capture_and_hint_sync( sentry_init, capture_events, httpx_mock, send_default_pii ): httpx_mock.add_response() @@ -107,47 +67,9 @@ def before_breadcrumb(crumb, hint): ) -@pytest.mark.asyncio -async def test_crumb_capture_and_hint_async(sentry_init, capture_events, httpx_mock): - httpx_mock.add_response() - - def before_breadcrumb(crumb, hint): - crumb["data"]["extra"] = "foo" - return crumb - - sentry_init(integrations=[HttpxIntegration()], before_breadcrumb=before_breadcrumb) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = await httpx.AsyncClient().get(url) - - assert response.status_code == 200 - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", - "extra": "foo", - } - ) - - @pytest.mark.asyncio @pytest.mark.parametrize("send_default_pii", [True, False]) -async def test_crumb_capture_and_hint_async_span_streaming( +async def test_crumb_capture_and_hint_async( sentry_init, capture_events, httpx_mock, send_default_pii ): httpx_mock.add_response() @@ -201,55 +123,6 @@ def before_breadcrumb(crumb, hint): ) -@pytest.mark.parametrize( - "status_code,level", - [ - (200, None), - (301, None), - (403, "warning"), - (405, "warning"), - (500, "error"), - ], -) -def test_crumb_capture_client_error_sync( - sentry_init, capture_events, httpx_mock, status_code, level -): - httpx_mock.add_response(status_code=status_code) - - sentry_init(integrations=[HttpxIntegration()]) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = httpx.Client().get(url) - - assert response.status_code == status_code - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: status_code, - } - ) - - @pytest.mark.parametrize( "status_code,level", [ @@ -261,7 +134,7 @@ def test_crumb_capture_client_error_sync( ], ) @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_client_error_sync_span_streaming( +def test_crumb_capture_client_error_sync( sentry_init, capture_events, httpx_mock, status_code, level, send_default_pii ): httpx_mock.add_response(status_code=status_code) @@ -312,56 +185,6 @@ def test_crumb_capture_client_error_sync_span_streaming( ) -@pytest.mark.asyncio -@pytest.mark.parametrize( - "status_code,level", - [ - (200, None), - (301, None), - (403, "warning"), - (405, "warning"), - (500, "error"), - ], -) -async def test_crumb_capture_client_error_async( - sentry_init, capture_events, httpx_mock, status_code, level -): - httpx_mock.add_response(status_code=status_code) - - sentry_init(integrations=[HttpxIntegration()]) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = await httpx.AsyncClient().get(url) - - assert response.status_code == status_code - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: status_code, - } - ) - - @pytest.mark.asyncio @pytest.mark.parametrize( "status_code,level", @@ -374,7 +197,7 @@ async def test_crumb_capture_client_error_async( ], ) @pytest.mark.parametrize("send_default_pii", [True, False]) -async def test_crumb_capture_client_error_async_span_streaming( +async def test_crumb_capture_client_error_async( sentry_init, capture_events, httpx_mock, status_code, level, send_default_pii ): httpx_mock.add_response(status_code=status_code) diff --git a/tests/integrations/httpx2/test_httpx2.py b/tests/integrations/httpx2/test_httpx2.py index ded51d7d79..3d02d0a132 100644 --- a/tests/integrations/httpx2/test_httpx2.py +++ b/tests/integrations/httpx2/test_httpx2.py @@ -11,48 +11,8 @@ from tests.conftest import ApproxDict -def test_crumb_capture_and_hint_sync(sentry_init, capture_events, httpx2_mock): - httpx2_mock.add_response() - - def before_breadcrumb(crumb, hint): - crumb["data"]["extra"] = "foo" - return crumb - - sentry_init( - integrations=[Httpx2Integration()], - before_breadcrumb=before_breadcrumb, - ) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = httpx2.Client().get(url) - - assert response.status_code == 200 - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", - "extra": "foo", - } - ) - - @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_and_hint_sync_span_streaming( +def test_crumb_capture_and_hint_sync( sentry_init, capture_events, httpx2_mock, send_default_pii ): httpx2_mock.add_response() @@ -107,47 +67,9 @@ def before_breadcrumb(crumb, hint): ) -@pytest.mark.asyncio -async def test_crumb_capture_and_hint_async(sentry_init, capture_events, httpx2_mock): - httpx2_mock.add_response() - - def before_breadcrumb(crumb, hint): - crumb["data"]["extra"] = "foo" - return crumb - - sentry_init(integrations=[Httpx2Integration()], before_breadcrumb=before_breadcrumb) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = await httpx2.AsyncClient().get(url) - - assert response.status_code == 200 - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", - "extra": "foo", - } - ) - - @pytest.mark.asyncio @pytest.mark.parametrize("send_default_pii", [True, False]) -async def test_crumb_capture_and_hint_async_span_streaming( +async def test_crumb_capture_and_hint_async( sentry_init, capture_events, httpx2_mock, send_default_pii ): httpx2_mock.add_response() @@ -201,55 +123,6 @@ def before_breadcrumb(crumb, hint): ) -@pytest.mark.parametrize( - "status_code,level", - [ - (200, None), - (301, None), - (403, "warning"), - (405, "warning"), - (500, "error"), - ], -) -def test_crumb_capture_client_error_sync( - sentry_init, capture_events, httpx2_mock, status_code, level -): - httpx2_mock.add_response(status_code=status_code) - - sentry_init(integrations=[Httpx2Integration()]) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = httpx2.Client().get(url) - - assert response.status_code == status_code - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: status_code, - } - ) - - @pytest.mark.parametrize( "status_code,level", [ @@ -261,7 +134,7 @@ def test_crumb_capture_client_error_sync( ], ) @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_client_error_sync_span_streaming( +def test_crumb_capture_client_error_sync( sentry_init, capture_events, httpx2_mock, status_code, level, send_default_pii ): httpx2_mock.add_response(status_code=status_code) @@ -312,56 +185,6 @@ def test_crumb_capture_client_error_sync_span_streaming( ) -@pytest.mark.asyncio -@pytest.mark.parametrize( - "status_code,level", - [ - (200, None), - (301, None), - (403, "warning"), - (405, "warning"), - (500, "error"), - ], -) -async def test_crumb_capture_client_error_async( - sentry_init, capture_events, httpx2_mock, status_code, level -): - httpx2_mock.add_response(status_code=status_code) - - sentry_init(integrations=[Httpx2Integration()]) - - url = "http://example.com/" - - with start_transaction(): - events = capture_events() - - response = await httpx2.AsyncClient().get(url) - - assert response.status_code == status_code - capture_message("Testing!") - - (event,) = events - - crumb = event["breadcrumbs"]["values"][0] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: status_code, - } - ) - - @pytest.mark.asyncio @pytest.mark.parametrize( "status_code,level", @@ -374,7 +197,7 @@ async def test_crumb_capture_client_error_async( ], ) @pytest.mark.parametrize("send_default_pii", [True, False]) -async def test_crumb_capture_client_error_async_span_streaming( +async def test_crumb_capture_client_error_async( sentry_init, capture_events, httpx2_mock, status_code, level, send_default_pii ): httpx2_mock.add_response(status_code=status_code) diff --git a/tests/integrations/requests/test_requests.py b/tests/integrations/requests/test_requests.py index e0db8133ad..0af67d6d07 100644 --- a/tests/integrations/requests/test_requests.py +++ b/tests/integrations/requests/test_requests.py @@ -11,32 +11,8 @@ PORT = create_mock_http_server() -def test_crumb_capture(sentry_init, capture_events): - sentry_init(integrations=[StdlibIntegration()], send_default_pii=True) - events = capture_events() - - url = f"http://localhost:{PORT}/hello-world" # noqa:E231 - response = requests.get(url) - capture_message("Testing!") - - (event,) = events - (crumb,) = event["breadcrumbs"]["values"] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_REQUEST_METHOD: "GET", - SPANDATA.URL_FRAGMENT: "", - SPANDATA.URL_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: response.status_code, - "reason": response.reason, - } - ) - - @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_span_streaming(sentry_init, capture_events, send_default_pii): +def test_crumb_capture(sentry_init, capture_events, send_default_pii): sentry_init( integrations=[StdlibIntegration()], send_default_pii=send_default_pii, @@ -72,50 +48,6 @@ def test_crumb_capture_span_streaming(sentry_init, capture_events, send_default_ ) -@pytest.mark.parametrize( - "status_code,level", - [ - (200, None), - (301, None), - (403, "warning"), - (405, "warning"), - (500, "error"), - ], -) -def test_crumb_capture_client_error(sentry_init, capture_events, status_code, level): - sentry_init(integrations=[StdlibIntegration()], send_default_pii=True) - - events = capture_events() - - url = f"http://localhost:{PORT}/status/{status_code}" # noqa:E231 - response = requests.get(url) - - assert response.status_code == status_code - - capture_message("Testing!") - - (event,) = events - (crumb,) = event["breadcrumbs"]["values"] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - - assert crumb["data"] == ApproxDict( - { - "url": url, - SPANDATA.HTTP_METHOD: "GET", - SPANDATA.HTTP_FRAGMENT: "", - SPANDATA.HTTP_QUERY: "", - SPANDATA.HTTP_STATUS_CODE: response.status_code, - "reason": response.reason, - } - ) - - @pytest.mark.parametrize( "status_code,level", [ @@ -127,7 +59,7 @@ def test_crumb_capture_client_error(sentry_init, capture_events, status_code, le ], ) @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_client_error_span_streaming( +def test_crumb_capture_client_error( sentry_init, capture_events, status_code, level, send_default_pii ): sentry_init( diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index 661b75d3c6..3cc502cc51 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -76,34 +76,8 @@ def create_chunked_server(): CHUNKED_PORT = create_chunked_server() -def test_crumb_capture(sentry_init, capture_events): - sentry_init(integrations=[StdlibIntegration()], send_default_pii=True) - events = capture_events() - - url = "http://localhost:{}/some/random/url".format(PORT) - urlopen(url) - - capture_message("Testing!") - - (event,) = events - (crumb,) = event["breadcrumbs"]["values"] - - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - SPANDATA.URL_FULL: url, - SPANDATA.HTTP_REQUEST_METHOD: "GET", - SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", - SPANDATA.URL_FRAGMENT: "", - SPANDATA.URL_QUERY: "", - } - ) - - @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_span_streaming(sentry_init, capture_events, send_default_pii): +def test_crumb_capture(sentry_init, capture_events, send_default_pii): sentry_init( integrations=[StdlibIntegration()], trace_lifecycle="stream", @@ -141,50 +115,6 @@ def test_crumb_capture_span_streaming(sentry_init, capture_events, send_default_ ) -@pytest.mark.parametrize( - "status_code,level", - [ - (200, None), - (301, None), - (403, "warning"), - (405, "warning"), - (500, "error"), - ], -) -def test_crumb_capture_client_error(sentry_init, capture_events, status_code, level): - sentry_init(integrations=[StdlibIntegration()], send_default_pii=True) - events = capture_events() - - url = f"http://localhost:{PORT}/status/{status_code}" # noqa:E231 - try: - urlopen(url) - except HTTPError: - pass - - capture_message("Testing!") - - (event,) = events - (crumb,) = event["breadcrumbs"]["values"] - - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - - if level is None: - assert "level" not in crumb - else: - assert crumb["level"] == level - - assert crumb["data"] == ApproxDict( - { - SPANDATA.URL_FULL: url, - SPANDATA.HTTP_REQUEST_METHOD: "GET", - SPANDATA.HTTP_STATUS_CODE: status_code, - SPANDATA.URL_FRAGMENT: "", - SPANDATA.URL_QUERY: "", - } - ) - - @pytest.mark.parametrize( "status_code,level", [ @@ -196,7 +126,7 @@ def test_crumb_capture_client_error(sentry_init, capture_events, status_code, le ], ) @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_client_error_span_streaming( +def test_crumb_capture_client_error( sentry_init, capture_events, status_code, level, send_default_pii ): sentry_init( @@ -244,44 +174,8 @@ def test_crumb_capture_client_error_span_streaming( ) -def test_crumb_capture_hint(sentry_init, capture_events): - def before_breadcrumb(crumb, hint): - crumb["data"]["extra"] = "foo" - return crumb - - sentry_init( - integrations=[StdlibIntegration()], - before_breadcrumb=before_breadcrumb, - send_default_pii=True, - ) - events = capture_events() - - url = "http://localhost:{}/some/random/url".format(PORT) - urlopen(url) - - capture_message("Testing!") - - (event,) = events - (crumb,) = event["breadcrumbs"]["values"] - assert crumb["type"] == "http" - assert crumb["category"] == "httplib" - assert crumb["data"] == ApproxDict( - { - SPANDATA.URL_FULL: url, - SPANDATA.HTTP_REQUEST_METHOD: "GET", - SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", - "extra": "foo", - SPANDATA.URL_FRAGMENT: "", - SPANDATA.URL_QUERY: "", - } - ) - - @pytest.mark.parametrize("send_default_pii", [True, False]) -def test_crumb_capture_hint_span_streaming( - sentry_init, capture_events, send_default_pii -): +def test_crumb_capture_hint(sentry_init, capture_events, send_default_pii): def before_breadcrumb(crumb, hint): crumb["data"]["extra"] = "foo" return crumb From 0500e8fe3e90475c82c31d91a7690073ca8e4de4 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 09:53:06 +0200 Subject: [PATCH 5/7] adapt another breadcrumb test --- tests/integrations/httpx/test_httpx.py | 3 +-- tests/integrations/httpx2/test_httpx2.py | 3 +-- tests/integrations/requests/test_requests.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index 838ffcdb84..fdcc063840 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -573,9 +573,8 @@ def test_omit_url_data_if_parsing_fails(sentry_init, capture_events, httpx_mock) (event,) = events assert event["breadcrumbs"]["values"][0]["data"] == ApproxDict( { - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", # no url related data } ) diff --git a/tests/integrations/httpx2/test_httpx2.py b/tests/integrations/httpx2/test_httpx2.py index 3d02d0a132..26cf87cdff 100644 --- a/tests/integrations/httpx2/test_httpx2.py +++ b/tests/integrations/httpx2/test_httpx2.py @@ -573,9 +573,8 @@ def test_omit_url_data_if_parsing_fails(sentry_init, capture_events, httpx2_mock (event,) = events assert event["breadcrumbs"]["values"][0]["data"] == ApproxDict( { - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", # no url related data } ) diff --git a/tests/integrations/requests/test_requests.py b/tests/integrations/requests/test_requests.py index 0af67d6d07..9cd7cadf11 100644 --- a/tests/integrations/requests/test_requests.py +++ b/tests/integrations/requests/test_requests.py @@ -125,9 +125,8 @@ def test_omit_url_data_if_parsing_fails(sentry_init, capture_events): (event,) = events assert event["breadcrumbs"]["values"][0]["data"] == ApproxDict( { - SPANDATA.HTTP_METHOD: "GET", + SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: response.status_code, - "reason": response.reason, # no url related data } ) From f5b2728cf0e5f861d4a936241037cbbfb52b67a5 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 11:15:01 +0200 Subject: [PATCH 6/7] fix tests --- tests/integrations/httpx/test_httpx.py | 3 ++- tests/integrations/httpx2/test_httpx2.py | 3 ++- tests/integrations/stdlib/test_httplib.py | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index fdcc063840..0ba0e99958 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -573,9 +573,10 @@ def test_omit_url_data_if_parsing_fails(sentry_init, capture_events, httpx_mock) (event,) = events assert event["breadcrumbs"]["values"][0]["data"] == ApproxDict( { - SPANDATA.HTTP_REQUEST_METHOD: "GET", + SPANDATA.HTTP_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, # no url related data + "reason": "OK", } ) diff --git a/tests/integrations/httpx2/test_httpx2.py b/tests/integrations/httpx2/test_httpx2.py index 26cf87cdff..71a97673a1 100644 --- a/tests/integrations/httpx2/test_httpx2.py +++ b/tests/integrations/httpx2/test_httpx2.py @@ -573,9 +573,10 @@ def test_omit_url_data_if_parsing_fails(sentry_init, capture_events, httpx2_mock (event,) = events assert event["breadcrumbs"]["values"][0]["data"] == ApproxDict( { - SPANDATA.HTTP_REQUEST_METHOD: "GET", + SPANDATA.HTTP_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, # no url related data + "reason": "OK", } ) diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index 3cc502cc51..48a4e5be28 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -276,7 +276,6 @@ def test_httplib_misuse(sentry_init, capture_events, request): SPANDATA.URL_FULL: "http://localhost:{}/200".format(PORT), SPANDATA.HTTP_REQUEST_METHOD: "GET", SPANDATA.HTTP_STATUS_CODE: 200, - "reason": "OK", SPANDATA.URL_FRAGMENT: "", SPANDATA.URL_QUERY: "", } From 93b230e892778c53bea5515316eacaea4cfb4f55 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Mon, 24 Aug 2026 11:32:10 +0200 Subject: [PATCH 7/7] remove remaining legacy references --- sentry_sdk/integrations/stdlib.py | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py index bd33a63167..8765c6c422 100644 --- a/sentry_sdk/integrations/stdlib.py +++ b/sentry_sdk/integrations/stdlib.py @@ -10,7 +10,6 @@ from sentry_sdk.integrations import Integration from sentry_sdk.scope import add_global_event_processor, should_send_default_pii from sentry_sdk.traces import StreamedSpan -from sentry_sdk.tracing import Span from sentry_sdk.tracing_utils import ( EnvironHeaders, add_http_breadcrumb, @@ -28,7 +27,7 @@ ) if TYPE_CHECKING: - from typing import Any, Callable, Dict, List, Optional, Union + from typing import Any, Callable, Dict, List, Optional from sentry_sdk._types import Event, Hint @@ -61,15 +60,10 @@ def add_python_runtime_context( return event -def _complete_span(span: "Union[Span, StreamedSpan]") -> None: - if isinstance(span, StreamedSpan): - with capture_internal_exceptions(): - add_http_request_source(span) - span.end() - else: - span.finish() - with capture_internal_exceptions(): - add_http_request_source(span) +def _complete_span(span: "StreamedSpan") -> None: + with capture_internal_exceptions(): + add_http_request_source(span) + span.end() def _install_httplib() -> None: @@ -205,14 +199,8 @@ def getresponse(self: "HTTPConnection", *args: "Any", **kwargs: "Any") -> "Any": add_http_breadcrumb(status_code, breadcrumb) return rv - if isinstance(span, StreamedSpan): - span.status = "error" if status_code >= 400 else "ok" - span.set_attribute(SPANDATA.HTTP_STATUS_CODE, status_code) - elif isinstance(span, Span): - span.set_http_status(status_code) - span.set_data("reason", rv.reason) - if breadcrumb: - breadcrumb["reason"] = rv.reason + span.status = "error" if status_code >= 400 else "ok" + span.set_attribute(SPANDATA.HTTP_STATUS_CODE, status_code) # getresponse doesn't include actually reading the response body. This # is done in read(). So if the metadata/headers suggest there's a body to