From 10bc3fa93687620ea9ad61bf5894b529a28189d7 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Fri, 22 Nov 2024 15:32:51 +0100 Subject: [PATCH 1/3] Fix socket (#3819) --- sentry_sdk/integrations/socket.py | 4 +++- tests/integrations/socket/test_socket.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/socket.py b/sentry_sdk/integrations/socket.py index cba448c3a3..335fd643aa 100644 --- a/sentry_sdk/integrations/socket.py +++ b/sentry_sdk/integrations/socket.py @@ -59,7 +59,9 @@ def create_connection( origin=SocketIntegration.origin, only_if_parent=True, ) as span: - span.set_data("address", address) + host, port = address + span.set_data("address.host", host) + span.set_data("address.port", port) span.set_data("timeout", timeout) span.set_data("source_address", source_address) diff --git a/tests/integrations/socket/test_socket.py b/tests/integrations/socket/test_socket.py index 389256de33..e629114b2b 100644 --- a/tests/integrations/socket/test_socket.py +++ b/tests/integrations/socket/test_socket.py @@ -42,9 +42,9 @@ def test_create_connection_trace(sentry_init, capture_events): assert connect_span["description"] == "example.com:443" assert connect_span["data"] == ApproxDict( { - "address": ["example.com", 443], + "address.host": "example.com", + "address.port": 443, "timeout": timeout, - "source_address": None, } ) From 59f84d4e9513c72eaaeffea9da03915ab685d1c5 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Thu, 28 Nov 2024 13:58:04 +0100 Subject: [PATCH 2/3] Fix rust tracing (#3817) --- sentry_sdk/integrations/rust_tracing.py | 69 ++++++---------- sentry_sdk/tracing.py | 9 +- .../rust_tracing/test_rust_tracing.py | 82 ++++++++++--------- 3 files changed, 76 insertions(+), 84 deletions(-) diff --git a/sentry_sdk/integrations/rust_tracing.py b/sentry_sdk/integrations/rust_tracing.py index ae52c850c3..d394ba5712 100644 --- a/sentry_sdk/integrations/rust_tracing.py +++ b/sentry_sdk/integrations/rust_tracing.py @@ -37,11 +37,9 @@ import sentry_sdk from sentry_sdk.integrations import Integration from sentry_sdk.scope import should_send_default_pii -from sentry_sdk.tracing import Span as SentrySpan +from sentry_sdk.tracing import POTelSpan as SentrySpan from sentry_sdk.utils import SENSITIVE_DATA_SUBSTITUTE -TraceState = Optional[Tuple[Optional[SentrySpan], SentrySpan]] - class RustTracingLevel(Enum): Trace: str = "TRACE" @@ -171,7 +169,7 @@ def _include_tracing_fields(self) -> bool: else self.include_tracing_fields ) - def on_event(self, event: str, _span_state: TraceState) -> None: + def on_event(self, event: str, _span_state: Optional[SentrySpan]) -> None: deserialized_event = json.loads(event) metadata = deserialized_event.get("metadata", {}) @@ -185,7 +183,7 @@ def on_event(self, event: str, _span_state: TraceState) -> None: elif event_type == EventTypeMapping.Event: process_event(deserialized_event) - def on_new_span(self, attrs: str, span_id: str) -> TraceState: + def on_new_span(self, attrs: str, span_id: str) -> Optional[SentrySpan]: attrs = json.loads(attrs) metadata = attrs.get("metadata", {}) @@ -205,48 +203,35 @@ def on_new_span(self, attrs: str, span_id: str) -> TraceState: else: sentry_span_name = "" - kwargs = { - "op": "function", - "name": sentry_span_name, - "origin": self.origin, - } - - scope = sentry_sdk.get_current_scope() - parent_sentry_span = scope.span - if parent_sentry_span: - sentry_span = parent_sentry_span.start_child(**kwargs) - else: - sentry_span = scope.start_span(**kwargs) + span = sentry_sdk.start_span( + op="function", + name=sentry_span_name, + origin=self.origin, + only_if_parent=True, + ) + span.__enter__() fields = metadata.get("fields", []) for field in fields: if self._include_tracing_fields(): - sentry_span.set_data(field, attrs.get(field)) - else: - sentry_span.set_data(field, SENSITIVE_DATA_SUBSTITUTE) - - scope.span = sentry_span - return (parent_sentry_span, sentry_span) - - def on_close(self, span_id: str, span_state: TraceState) -> None: - if span_state is None: - return - - parent_sentry_span, sentry_span = span_state - sentry_span.finish() - sentry_sdk.get_current_scope().span = parent_sentry_span - - def on_record(self, span_id: str, values: str, span_state: TraceState) -> None: - if span_state is None: - return - _parent_sentry_span, sentry_span = span_state - - deserialized_values = json.loads(values) - for key, value in deserialized_values.items(): - if self._include_tracing_fields(): - sentry_span.set_data(key, value) + span.set_data(field, attrs.get(field)) else: - sentry_span.set_data(key, SENSITIVE_DATA_SUBSTITUTE) + span.set_data(field, SENSITIVE_DATA_SUBSTITUTE) + + return span + + def on_close(self, span_id: str, span: Optional[SentrySpan]) -> None: + if span is not None: + span.__exit__(None, None, None) + + def on_record(self, span_id: str, values: str, span: Optional[SentrySpan]) -> None: + if span is not None: + deserialized_values = json.loads(values) + for key, value in deserialized_values.items(): + if self._include_tracing_fields(): + span.set_data(key, value) + else: + span.set_data(key, SENSITIVE_DATA_SUBSTITUTE) class RustTracingIntegration(Integration): diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 59533106b8..4a4f28552b 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -1,3 +1,4 @@ +import json import uuid import random import time @@ -1631,8 +1632,12 @@ def finish(self, end_timestamp=None): def to_json(self): # type: () -> dict[str, Any] - # TODO-neel-potel for sampling context - pass + """ + Only meant for testing. Not used internally anymore. + """ + if not isinstance(self._otel_span, ReadableSpan): + return {} + return json.loads(self._otel_span.to_json()) def get_trace_context(self): # type: () -> dict[str, Any] diff --git a/tests/integrations/rust_tracing/test_rust_tracing.py b/tests/integrations/rust_tracing/test_rust_tracing.py index 893fc86966..77f07649b2 100644 --- a/tests/integrations/rust_tracing/test_rust_tracing.py +++ b/tests/integrations/rust_tracing/test_rust_tracing.py @@ -11,7 +11,8 @@ RustTracingLevel, EventTypeMapping, ) -from sentry_sdk import start_transaction, capture_message +from sentry_sdk import start_span, capture_message +from tests.conftest import ApproxDict def _test_event_type_mapping(metadata: Dict[str, object]) -> EventTypeMapping: @@ -74,11 +75,11 @@ def test_on_new_span_on_close(sentry_init, capture_events): sentry_init(integrations=[integration], traces_sample_rate=1.0) events = capture_events() - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) sentry_first_rust_span = sentry_sdk.get_current_span() - _, rust_first_rust_span = rust_tracing.spans[3] + rust_first_rust_span = rust_tracing.spans[3] assert sentry_first_rust_span == rust_first_rust_span @@ -102,7 +103,7 @@ def test_on_new_span_on_close(sentry_init, capture_events): data = span["data"] assert data["use_memoized"] assert data["index"] == 10 - assert data["version"] is None + assert "version" not in data def test_nested_on_new_span_on_close(sentry_init, capture_events): @@ -115,23 +116,20 @@ def test_nested_on_new_span_on_close(sentry_init, capture_events): sentry_init(integrations=[integration], traces_sample_rate=1.0) events = capture_events() - with start_transaction(): + with start_span(): original_sentry_span = sentry_sdk.get_current_span() rust_tracing.new_span(RustTracingLevel.Info, 3, index_arg=10) sentry_first_rust_span = sentry_sdk.get_current_span() - _, rust_first_rust_span = rust_tracing.spans[3] + rust_first_rust_span = rust_tracing.spans[3] # Use a different `index_arg` value for the inner span to help # distinguish the two at the end of the test rust_tracing.new_span(RustTracingLevel.Info, 5, index_arg=9) sentry_second_rust_span = sentry_sdk.get_current_span() - rust_parent_span, rust_second_rust_span = rust_tracing.spans[5] + rust_second_rust_span = rust_tracing.spans[5] assert rust_second_rust_span == sentry_second_rust_span - assert rust_parent_span == sentry_first_rust_span - assert rust_parent_span == rust_first_rust_span - assert rust_parent_span != rust_second_rust_span rust_tracing.close_span(5) @@ -171,12 +169,12 @@ def test_nested_on_new_span_on_close(sentry_init, capture_events): first_span_data = first_span["data"] assert first_span_data["use_memoized"] assert first_span_data["index"] == 10 - assert first_span_data["version"] is None + assert "version" not in first_span_data second_span_data = second_span["data"] assert second_span_data["use_memoized"] assert second_span_data["index"] == 9 - assert second_span_data["version"] is None + assert "version" not in second_span_data def test_on_new_span_without_transaction(sentry_init): @@ -207,7 +205,7 @@ def test_on_event_exception(sentry_init, capture_events): events = capture_events() sentry_sdk.get_isolation_scope().clear_breadcrumbs() - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) # Mapped to Exception @@ -243,7 +241,7 @@ def test_on_event_breadcrumb(sentry_init, capture_events): events = capture_events() sentry_sdk.get_isolation_scope().clear_breadcrumbs() - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) # Mapped to Breadcrumb @@ -274,7 +272,7 @@ def test_on_event_event(sentry_init, capture_events): events = capture_events() sentry_sdk.get_isolation_scope().clear_breadcrumbs() - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) # Mapped to Event @@ -311,7 +309,7 @@ def test_on_event_ignored(sentry_init, capture_events): events = capture_events() sentry_sdk.get_isolation_scope().clear_breadcrumbs() - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) # Ignored @@ -344,7 +342,7 @@ def span_filter(metadata: Dict[str, object]) -> bool: sentry_init(integrations=[integration], traces_sample_rate=1.0) events = capture_events() - with start_transaction(): + with start_span(): original_sentry_span = sentry_sdk.get_current_span() # Span is not ignored @@ -377,16 +375,16 @@ def test_record(sentry_init): ) sentry_init(integrations=[integration], traces_sample_rate=1.0) - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) span_before_record = sentry_sdk.get_current_span().to_json() - assert span_before_record["data"]["version"] is None + assert "version" not in span_before_record["attributes"] rust_tracing.record(3) span_after_record = sentry_sdk.get_current_span().to_json() - assert span_after_record["data"]["version"] == "memoized" + assert span_after_record["attributes"]["version"] == "memoized" def test_record_in_ignored_span(sentry_init): @@ -403,18 +401,18 @@ def span_filter(metadata: Dict[str, object]) -> bool: ) sentry_init(integrations=[integration], traces_sample_rate=1.0) - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) span_before_record = sentry_sdk.get_current_span().to_json() - assert span_before_record["data"]["version"] is None + assert "version" not in span_before_record["attributes"] rust_tracing.new_span(RustTracingLevel.Trace, 5) rust_tracing.record(5) # `on_record()` should not do anything to the current Sentry span if the associated Rust span was ignored span_after_record = sentry_sdk.get_current_span().to_json() - assert span_after_record["data"]["version"] is None + assert "version" not in span_after_record["attributes"] @pytest.mark.parametrize( @@ -443,33 +441,37 @@ def test_include_tracing_fields( traces_sample_rate=1.0, send_default_pii=send_default_pii, ) - with start_transaction(): + with start_span(): rust_tracing.new_span(RustTracingLevel.Info, 3) span_before_record = sentry_sdk.get_current_span().to_json() if tracing_fields_expected: - assert span_before_record["data"]["version"] is None + assert "version" not in span_before_record["attributes"] else: - assert span_before_record["data"]["version"] == "[Filtered]" + assert span_before_record["attributes"]["version"] == "[Filtered]" rust_tracing.record(3) span_after_record = sentry_sdk.get_current_span().to_json() if tracing_fields_expected: - assert span_after_record["data"] == { - "thread.id": mock.ANY, - "thread.name": mock.ANY, - "use_memoized": True, - "version": "memoized", - "index": 10, - } + assert span_after_record["attributes"] == ApproxDict( + { + "thread.id": mock.ANY, + "thread.name": mock.ANY, + "use_memoized": True, + "version": "memoized", + "index": 10, + } + ) else: - assert span_after_record["data"] == { - "thread.id": mock.ANY, - "thread.name": mock.ANY, - "use_memoized": "[Filtered]", - "version": "[Filtered]", - "index": "[Filtered]", - } + assert span_after_record["attributes"] == ApproxDict( + { + "thread.id": mock.ANY, + "thread.name": mock.ANY, + "use_memoized": "[Filtered]", + "version": "[Filtered]", + "index": "[Filtered]", + } + ) From 7c2d77002652047d609a97858d0135625c6d5037 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Thu, 28 Nov 2024 14:00:32 +0100 Subject: [PATCH 3/3] Fix httplib (#3820) --- .../integrations/opentelemetry/utils.py | 53 +++--- sentry_sdk/integrations/stdlib.py | 3 +- tests/integrations/httpx/test_httpx.py | 10 +- tests/integrations/stdlib/test_httplib.py | 157 ++++++++---------- 4 files changed, 110 insertions(+), 113 deletions(-) diff --git a/sentry_sdk/integrations/opentelemetry/utils.py b/sentry_sdk/integrations/opentelemetry/utils.py index 673b334318..1db3f65da1 100644 --- a/sentry_sdk/integrations/opentelemetry/utils.py +++ b/sentry_sdk/integrations/opentelemetry/utils.py @@ -161,36 +161,43 @@ def span_data_for_http_method(span): # type: (ReadableSpan) -> OtelExtractedSpanData span_attributes = span.attributes or {} - op = "http" + op = span_attributes.get(SentrySpanAttribute.OP) + if op is None: + op = "http" - if span.kind == SpanKind.SERVER: - op += ".server" - elif span.kind == SpanKind.CLIENT: - op += ".client" + if span.kind == SpanKind.SERVER: + op += ".server" + elif span.kind == SpanKind.CLIENT: + op += ".client" http_method = span_attributes.get(SpanAttributes.HTTP_METHOD) route = span_attributes.get(SpanAttributes.HTTP_ROUTE) target = span_attributes.get(SpanAttributes.HTTP_TARGET) peer_name = span_attributes.get(SpanAttributes.NET_PEER_NAME) - description = f"{http_method}" - - if route: - description = f"{http_method} {route}" - elif target: - description = f"{http_method} {target}" - elif peer_name: - description = f"{http_method} {peer_name}" - else: - url = span_attributes.get(SpanAttributes.HTTP_URL) - url = cast("Optional[str]", url) - - if url: - parsed_url = urlparse(url) - url = "{}://{}{}".format( - parsed_url.scheme, parsed_url.netloc, parsed_url.path - ) - description = f"{http_method} {url}" + # TODO-neel-potel remove description completely + description = span_attributes.get( + SentrySpanAttribute.DESCRIPTION + ) or span_attributes.get(SentrySpanAttribute.NAME) + if description is None: + description = f"{http_method}" + + if route: + description = f"{http_method} {route}" + elif target: + description = f"{http_method} {target}" + elif peer_name: + description = f"{http_method} {peer_name}" + else: + url = span_attributes.get(SpanAttributes.HTTP_URL) + url = cast("Optional[str]", url) + + if url: + parsed_url = urlparse(url) + url = "{}://{}{}".format( + parsed_url.scheme, parsed_url.netloc, parsed_url.path + ) + description = f"{http_method} {url}" status, http_status = extract_span_status(span) diff --git a/sentry_sdk/integrations/stdlib.py b/sentry_sdk/integrations/stdlib.py index 424e7b88aa..7b704593db 100644 --- a/sentry_sdk/integrations/stdlib.py +++ b/sentry_sdk/integrations/stdlib.py @@ -96,6 +96,7 @@ def putrequest(self, method, url, *args, **kwargs): origin="auto.http.stdlib.httplib", only_if_parent=True, ) + span.__enter__() data = { SPANDATA.HTTP_METHOD: method, @@ -152,7 +153,7 @@ def getresponse(self, *args, **kwargs): span.set_http_status(int(rv.status)) span.set_data("reason", rv.reason) finally: - span.finish() + span.__exit__(None, None, None) return rv diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index 440171e8c4..9890d1f0cc 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -101,7 +101,10 @@ def test_outgoing_trace_headers(sentry_init, httpx_client, capture_envelopes): (httpx.Client(), httpx.AsyncClient()), ) def test_outgoing_trace_headers_append_to_baggage( - sentry_init, httpx_client, capture_envelopes, SortedBaggage, # noqa: N803 + sentry_init, + httpx_client, + capture_envelopes, + SortedBaggage, # noqa: N803 ): sentry_init( traces_sample_rate=1.0, @@ -137,9 +140,8 @@ def test_outgoing_trace_headers_append_to_baggage( parent_span_id=request_span["span_id"], sampled=1, ) - assert ( - response.request.headers["baggage"] - == SortedBaggage(f"custom=data,sentry-trace_id={trace_id},sentry-environment=production,sentry-release=d08ebdb9309e1b004c6f52202de58a09c2268e42,sentry-transaction=/interactions/other-dogs/new-dog,sentry-sample_rate=1.0,sentry-sampled=true") + assert response.request.headers["baggage"] == SortedBaggage( + f"custom=data,sentry-trace_id={trace_id},sentry-environment=production,sentry-release=d08ebdb9309e1b004c6f52202de58a09c2268e42,sentry-transaction=/interactions/other-dogs/new-dog,sentry-sample_rate=1.0,sentry-sampled=true" ) diff --git a/tests/integrations/stdlib/test_httplib.py b/tests/integrations/stdlib/test_httplib.py index ab0e04eadc..642c707268 100644 --- a/tests/integrations/stdlib/test_httplib.py +++ b/tests/integrations/stdlib/test_httplib.py @@ -6,7 +6,7 @@ import pytest -from sentry_sdk import capture_message, start_transaction +from sentry_sdk import capture_message, start_span, continue_trace, isolation_scope from sentry_sdk.consts import MATCH_ALL, SPANDATA from sentry_sdk.tracing import Transaction from sentry_sdk.integrations.stdlib import StdlibIntegration @@ -16,6 +16,31 @@ PORT = create_mock_http_server() +@pytest.fixture +def capture_request_headers(monkeypatch): + """ + HTTPConnection.send is passed a string containing (among other things) + the headers on the request. Mock it so we can check the headers. + """ + + def inner(do_send=True): + request_headers = {} + old_send = HTTPConnection.send + + def patched_send(self, data): + for line in data.decode("utf-8").split("\r\n")[1:]: + if line: + key, val = line.split(": ") + request_headers[key] = val + if do_send: + old_send(self, data) + + monkeypatch.setattr(HTTPConnection, "send", patched_send) + return request_headers + + return inner + + def test_crumb_capture(sentry_init, capture_events): sentry_init(integrations=[StdlibIntegration()]) events = capture_events() @@ -79,7 +104,7 @@ def test_empty_realurl(sentry_init): """ sentry_init(dsn="") - HTTPConnection("example.com", port=443).putrequest("POST", None) + HTTPConnection("localhost", PORT).putrequest("POST", None) def test_httplib_misuse(sentry_init, capture_events, request): @@ -131,40 +156,28 @@ def test_httplib_misuse(sentry_init, capture_events, request): ) -def test_outgoing_trace_headers(sentry_init, monkeypatch, capture_envelopes): - # HTTPSConnection.send is passed a string containing (among other things) - # the headers on the request. Mock it so we can check the headers, and also - # so it doesn't try to actually talk to the internet. - mock_send = mock.Mock() - monkeypatch.setattr(HTTPSConnection, "send", mock_send) - +def test_outgoing_trace_headers( + sentry_init, capture_envelopes, capture_request_headers, SortedBaggage +): sentry_init(traces_sample_rate=1.0) - envelopes = capture_envelopes() + request_headers = capture_request_headers() headers = {} + headers["sentry-trace"] = "771a43a4192642f0b136d5159a501700-1234567890abcdef-1" headers["baggage"] = ( "other-vendor-value-1=foo;bar;baz, sentry-trace_id=771a43a4192642f0b136d5159a501700, " - "sentry-public_key=49d0f7386ad645858ae85020e393bef3, sentry-sample_rate=0.01337, " + "sentry-public_key=49d0f7386ad645858ae85020e393bef3, " + "sentry-sampled=true, sentry-sample_rate=0.01337, " "sentry-user_id=Am%C3%A9lie, other-vendor-value-2=foo;bar;" ) - transaction = Transaction.continue_from_headers(headers) - - with start_transaction( - transaction=transaction, - name="/interactions/other-dogs/new-dog", - op="greeting.sniff", - trace_id="12312012123120121231201212312012", - ) as transaction: - HTTPSConnection("www.squirrelchasers.com").request("GET", "/top-chasers") - - (request_str,) = mock_send.call_args[0] - request_headers = {} - for line in request_str.decode("utf-8").split("\r\n")[1:]: - if line: - key, val = line.split(": ") - request_headers[key] = val + with isolation_scope(): + with continue_trace(headers): + with start_span(name="/interactions/other-dogs/new-dog"): + conn = HTTPConnection("localhost", PORT) + conn.request("GET", "/top-chasers") + conn.getresponse() (envelope,) = envelopes transaction = envelope.get_transaction_event() @@ -180,38 +193,30 @@ def test_outgoing_trace_headers(sentry_init, monkeypatch, capture_envelopes): expected_outgoing_baggage = ( "sentry-trace_id=771a43a4192642f0b136d5159a501700," "sentry-public_key=49d0f7386ad645858ae85020e393bef3," + "sentry-sampled=true," "sentry-sample_rate=0.01337," "sentry-user_id=Am%C3%A9lie" ) - assert request_headers["baggage"] == expected_outgoing_baggage + assert request_headers["baggage"] == SortedBaggage(expected_outgoing_baggage) -def test_outgoing_trace_headers_head_sdk(sentry_init, monkeypatch, capture_envelopes): - # HTTPSConnection.send is passed a string containing (among other things) - # the headers on the request. Mock it so we can check the headers, and also - # so it doesn't try to actually talk to the internet. - mock_send = mock.Mock() - monkeypatch.setattr(HTTPSConnection, "send", mock_send) - +def test_outgoing_trace_headers_head_sdk( + sentry_init, monkeypatch, capture_request_headers, capture_envelopes, SortedBaggage +): # make sure transaction is always sampled monkeypatch.setattr(random, "random", lambda: 0.1) sentry_init(traces_sample_rate=0.5, release="foo") - envelopes = capture_envelopes() + request_headers = capture_request_headers() - transaction = Transaction.continue_from_headers({}) - - with start_transaction(transaction=transaction, name="Head SDK tx") as transaction: - HTTPSConnection("www.squirrelchasers.com").request("GET", "/top-chasers") - - (request_str,) = mock_send.call_args[0] - request_headers = {} - for line in request_str.decode("utf-8").split("\r\n")[1:]: - if line: - key, val = line.split(": ") - request_headers[key] = val + with isolation_scope(): + with continue_trace({}): + with start_span(name="Head SDK tx") as root_span: + conn = HTTPConnection("localhost", PORT) + conn.request("GET", "/top-chasers") + conn.getresponse() (envelope,) = envelopes transaction = envelope.get_transaction_event() @@ -225,14 +230,15 @@ def test_outgoing_trace_headers_head_sdk(sentry_init, monkeypatch, capture_envel assert request_headers["sentry-trace"] == expected_sentry_trace expected_outgoing_baggage = ( - "sentry-trace_id=%s," + f"sentry-trace_id={root_span.trace_id}," "sentry-environment=production," "sentry-release=foo," "sentry-sample_rate=0.5," - "sentry-sampled=%s" - ) % (transaction.trace_id, "true" if transaction.sampled else "false") + "sentry-sampled=true," + "sentry-transaction=Head%20SDK%20tx" + ) - assert request_headers["baggage"] == expected_outgoing_baggage + assert request_headers["baggage"] == SortedBaggage(expected_outgoing_baggage) @pytest.mark.parametrize( @@ -295,42 +301,23 @@ def test_outgoing_trace_headers_head_sdk(sentry_init, monkeypatch, capture_envel ], ) def test_option_trace_propagation_targets( - sentry_init, monkeypatch, trace_propagation_targets, host, path, trace_propagated + sentry_init, + capture_request_headers, + trace_propagation_targets, + host, + path, + trace_propagated, ): - # HTTPSConnection.send is passed a string containing (among other things) - # the headers on the request. Mock it so we can check the headers, and also - # so it doesn't try to actually talk to the internet. - mock_send = mock.Mock() - monkeypatch.setattr(HTTPSConnection, "send", mock_send) - sentry_init( trace_propagation_targets=trace_propagation_targets, traces_sample_rate=1.0, ) - headers = { - "baggage": ( - "sentry-trace_id=771a43a4192642f0b136d5159a501700, " - "sentry-public_key=49d0f7386ad645858ae85020e393bef3, sentry-sample_rate=0.01337, " - ) - } - - transaction = Transaction.continue_from_headers(headers) - - with start_transaction( - transaction=transaction, - name="/interactions/other-dogs/new-dog", - op="greeting.sniff", - trace_id="12312012123120121231201212312012", - ) as transaction: - HTTPSConnection(host).request("GET", path) + request_headers = capture_request_headers(do_send=False) - (request_str,) = mock_send.call_args[0] - request_headers = {} - for line in request_str.decode("utf-8").split("\r\n")[1:]: - if line: - key, val = line.split(": ") - request_headers[key] = val + with start_span(name="foo"): + HTTPSConnection(host).request("GET", path) + # don't invoke getresponse to avoid actual network traffic if trace_propagated: assert "sentry-trace" in request_headers @@ -344,8 +331,8 @@ def test_span_origin(sentry_init, capture_events): sentry_init(traces_sample_rate=1.0, debug=True) events = capture_events() - with start_transaction(name="foo"): - conn = HTTPSConnection("example.com") + with start_span(name="foo"): + conn = HTTPConnection("localhost", PORT) conn.request("GET", "/foo") conn.getresponse() @@ -364,9 +351,9 @@ def test_http_timeout(monkeypatch, sentry_init, capture_envelopes): envelopes = capture_envelopes() - with start_transaction(op="op", name="name"): + with start_span(op="op", name="name"): try: - conn = HTTPSConnection("www.squirrelchasers.com") + conn = HTTPConnection("localhost", PORT) conn.request("GET", "/top-chasers") conn.getresponse() except Exception: @@ -385,4 +372,4 @@ def test_http_timeout(monkeypatch, sentry_init, capture_envelopes): span = transaction["spans"][0] assert span["op"] == "http.client" - assert span["description"] == "GET https://www.squirrelchasers.com/top-chasers" + assert span["description"] == f"GET http://localhost:{PORT}/top-chasers"