diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index 0001ff2591..89a2c69908 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -6,7 +6,6 @@ import sentry_sdk from sentry_sdk._werkzeug import _get_headers, get_host -from sentry_sdk.api import continue_trace from sentry_sdk.consts import OP, SPANDATA from sentry_sdk.data_collection import _apply_data_collection_filtering_to_query_string from sentry_sdk.integrations._wsgi_common import ( @@ -15,9 +14,7 @@ ) from sentry_sdk.scope import Scope, should_send_default_pii, use_isolation_scope from sentry_sdk.sessions import track_session -from sentry_sdk.traces import SegmentNameSource, StreamedSpan -from sentry_sdk.tracing import Span, TransactionSource -from sentry_sdk.tracing_utils import has_span_streaming_enabled +from sentry_sdk.traces import SegmentNameSource from sentry_sdk.utils import ( capture_internal_exceptions, event_from_exception, @@ -40,6 +37,7 @@ ) from sentry_sdk._types import Event, EventProcessor + from sentry_sdk.traces import StreamedSpan from sentry_sdk.utils import ExcInfo WsgiResponseIter = TypeVar("WsgiResponseIter") @@ -113,7 +111,6 @@ def __call__( return self.app(environ, start_response) client = sentry_sdk.get_client() - span_streaming = has_span_streaming_enabled(client.options) _wsgi_middleware_applied.set(True) try: @@ -130,60 +127,42 @@ def __call__( method = environ.get("REQUEST_METHOD", "").upper() - span_ctx: "Optional[ContextManager[Union[Span, StreamedSpan, None]]]" = None + span_ctx: "Optional[ContextManager[Union[StreamedSpan, None]]]" = ( + None + ) + if method in self.http_methods_to_capture: - if span_streaming: - sentry_sdk.traces.continue_trace( - dict(_get_headers(environ)) - ) - Scope.set_custom_sampling_context({"wsgi_environ": environ}) - - if has_data_collection_enabled(client.options): - if client.options["data_collection"]["user_info"]: - client_ip = get_client_ip(environ) - if client_ip: - scope.set_attribute( - SPANDATA.USER_IP_ADDRESS, client_ip - ) - elif should_send_default_pii(): + sentry_sdk.traces.continue_trace(dict(_get_headers(environ))) + Scope.set_custom_sampling_context({"wsgi_environ": environ}) + + if has_data_collection_enabled(client.options): + if client.options["data_collection"]["user_info"]: client_ip = get_client_ip(environ) if client_ip: scope.set_attribute( SPANDATA.USER_IP_ADDRESS, client_ip ) + elif should_send_default_pii(): + client_ip = get_client_ip(environ) + if client_ip: + scope.set_attribute(SPANDATA.USER_IP_ADDRESS, client_ip) + + span_ctx = sentry_sdk.traces.start_span( + name=_DEFAULT_TRANSACTION_NAME, + attributes={ + "sentry.segment.name.source": SegmentNameSource.ROUTE, + "sentry.origin": self.span_origin, + "sentry.op": OP.HTTP_SERVER, + }, + parent_span=None, + ) - span_ctx = sentry_sdk.traces.start_span( - name=_DEFAULT_TRANSACTION_NAME, - attributes={ - "sentry.segment.name.source": SegmentNameSource.ROUTE, - "sentry.origin": self.span_origin, - "sentry.op": OP.HTTP_SERVER, - }, - parent_span=None, - ) - else: - transaction = continue_trace( - environ, - op=OP.HTTP_SERVER, - name=_DEFAULT_TRANSACTION_NAME, - source=TransactionSource.ROUTE, - origin=self.span_origin, - ) - - span_ctx = sentry_sdk.start_transaction( - transaction, - custom_sampling_context={"wsgi_environ": environ}, - ) - - span_ctx = span_ctx or nullcontext() - - with span_ctx as span: - if isinstance(span, StreamedSpan): - with capture_internal_exceptions(): - for attr, value in _get_request_attributes( - environ, self.use_x_forwarded_for - ).items(): - span.set_attribute(attr, value) + with span_ctx or nullcontext() as span: + with capture_internal_exceptions(): + for attr, value in _get_request_attributes( + environ, self.use_x_forwarded_for + ).items(): + span.set_attribute(attr, value) try: response = self.app( @@ -223,7 +202,7 @@ def __call__( def _sentry_start_response( old_start_response: "StartResponse", - span: "Optional[Union[Span, StreamedSpan]]", + span: "Optional[StreamedSpan]", status: str, response_headers: "WsgiResponseHeaders", exc_info: "Optional[WsgiExcInfo]" = None, @@ -231,11 +210,8 @@ def _sentry_start_response( with capture_internal_exceptions(): status_int = int(status.split(" ", 1)[0]) if span is not None: - if isinstance(span, StreamedSpan): - span.status = "error" if status_int >= 400 else "ok" - span.set_attribute("http.response.status_code", status_int) - else: - span.set_http_status(status_int) + span.status = "error" if status_int >= 400 else "ok" + span.set_attribute("http.response.status_code", status_int) if exc_info is None: # The Django Rest Framework WSGI test client, and likely other diff --git a/tests/integrations/wsgi/test_wsgi.py b/tests/integrations/wsgi/test_wsgi.py index 6d15e6d529..b67cc96fe9 100644 --- a/tests/integrations/wsgi/test_wsgi.py +++ b/tests/integrations/wsgi/test_wsgi.py @@ -140,14 +140,10 @@ def test_keyboard_interrupt_is_captured(sentry_init, capture_events): assert event["level"] == "error" -@pytest.mark.parametrize("span_streaming", [True, False]) -def test_transaction_with_error( +def test_segment_with_error( sentry_init, crashing_app, - capture_events, capture_items, - DictionaryContaining, # noqa:N803 - span_streaming, ): def dogpark(environ, start_response): raise ValueError("Fetch aborted. The ball was not returned.") @@ -155,32 +151,24 @@ def dogpark(environ, start_response): sentry_init( send_default_pii=True, traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(dogpark) client = Client(app) - if span_streaming: - items = capture_items("event", "span") - else: - events = capture_events() + items = capture_items("event", "span") with pytest.raises(ValueError): client.get("http://dogs.are.great/sit/stay/rollover/") sentry_sdk.flush() - if span_streaming: - assert len(items) == 2 - assert items[0].type == "event" - assert items[1].type == "span" + assert len(items) == 2 + assert items[0].type == "event" + assert items[1].type == "span" - error_event = items[0].payload - span_item = items[1].payload - else: - error_event, envelope = events - - assert error_event["transaction"] == "generic WSGI request" + error_event = items[0].payload + span_item = items[1].payload assert error_event["contexts"]["trace"]["op"] == "http.server" assert error_event["exception"]["values"][0]["type"] == "ValueError" @@ -191,30 +179,15 @@ def dogpark(environ, start_response): == "Fetch aborted. The ball was not returned." ) - if span_streaming: - assert span_item["trace_id"] == error_event["contexts"]["trace"]["trace_id"] - assert span_item["span_id"] == error_event["contexts"]["trace"]["span_id"] - assert span_item["status"] == "error" - else: - assert envelope["type"] == "transaction" - - # event trace context is a subset of envelope trace context - assert envelope["contexts"]["trace"] == DictionaryContaining( - error_event["contexts"]["trace"] - ) - assert envelope["contexts"]["trace"]["status"] == "internal_error" - assert envelope["transaction"] == error_event["transaction"] - assert envelope["request"] == error_event["request"] + assert span_item["trace_id"] == error_event["contexts"]["trace"]["trace_id"] + assert span_item["span_id"] == error_event["contexts"]["trace"]["span_id"] + assert span_item["status"] == "error" @pytest.mark.parametrize("send_pii", [True, False]) -@pytest.mark.parametrize("span_streaming", [True, False]) -def test_transaction_no_error( +def test_segment_no_error( sentry_init, - capture_events, capture_items, - DictionaryContaining, # noqa:N803 - span_streaming, send_pii, ): def dogpark(environ, start_response): @@ -224,65 +197,44 @@ def dogpark(environ, start_response): sentry_init( send_default_pii=send_pii, traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(dogpark) client = Client(app) - if span_streaming: - items = capture_items("span") - else: - events = capture_events() + items = capture_items("span") client.get("/dogs/are/great?toy=tennisball") sentry_sdk.flush() - if span_streaming: - assert len(items) == 1 - span = items[0].payload - - assert span["is_segment"] is True - assert span["name"] == "generic WSGI request" - assert span["attributes"]["sentry.op"] == "http.server" - assert span["attributes"]["sentry.segment.name.source"] == "route" - assert span["attributes"]["http.request.method"] == "GET" - assert span["attributes"]["http.response.status_code"] == 200 - assert span["status"] == "ok" - - if send_pii: - assert ( - span["attributes"]["url.full"] - == "http://localhost/dogs/are/great?toy=tennisball" - ) - assert span["attributes"]["url.path"] == "/dogs/are/great" - assert span["attributes"]["http.query"] == "toy=tennisball" - else: - assert "url.path" not in span["attributes"] - assert "url.full" not in span["attributes"] - assert "http.query" not in span["attributes"] + assert len(items) == 1 + span = items[0].payload - else: - envelope = events[0] + assert span["is_segment"] is True + assert span["name"] == "generic WSGI request" + assert span["attributes"]["sentry.op"] == "http.server" + assert span["attributes"]["sentry.segment.name.source"] == "route" + assert span["attributes"]["http.request.method"] == "GET" + assert span["attributes"]["http.response.status_code"] == 200 + assert span["status"] == "ok" - assert envelope["type"] == "transaction" - assert envelope["transaction"] == "generic WSGI request" - assert envelope["contexts"]["trace"]["op"] == "http.server" - assert envelope["request"] == DictionaryContaining( - { - "method": "GET", - "url": "http://localhost/dogs/are/great", - "query_string": "toy=tennisball", - } + if send_pii: + assert ( + span["attributes"]["url.full"] + == "http://localhost/dogs/are/great?toy=tennisball" ) + assert span["attributes"]["url.path"] == "/dogs/are/great" + assert span["attributes"]["http.query"] == "toy=tennisball" + else: + assert "url.path" not in span["attributes"] + assert "url.full" not in span["attributes"] + assert "http.query" not in span["attributes"] -@pytest.mark.parametrize("span_streaming", [True, False]) def test_has_trace_if_performance_enabled( sentry_init, - capture_events, capture_items, - span_streaming, ): def dogpark(environ, start_response): capture_message("Attempting to fetch the ball") @@ -290,60 +242,39 @@ def dogpark(environ, start_response): sentry_init( traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(dogpark) client = Client(app) - if span_streaming: - items = capture_items("event", "span") - else: - events = capture_events() + items = capture_items("event", "span") with pytest.raises(ValueError): client.get("http://dogs.are.great/sit/stay/rollover/") sentry_sdk.flush() - if span_streaming: - msg_event, error_event, span_item = items - - assert msg_event.type == "event" - msg_event = msg_event.payload - assert msg_event["contexts"]["trace"] - assert "trace_id" in msg_event["contexts"]["trace"] - - assert error_event.type == "event" - error_event = error_event.payload - assert error_event["contexts"]["trace"] - assert "trace_id" in error_event["contexts"]["trace"] - - assert span_item.type == "span" - span_item = span_item.payload - assert span_item["trace_id"] is not None - - assert ( - msg_event["contexts"]["trace"]["trace_id"] - == error_event["contexts"]["trace"]["trace_id"] - == span_item["trace_id"] - ) - else: - msg_event, error_event, transaction_event = events + msg_event, error_event, span_item = items - assert msg_event["contexts"]["trace"] - assert "trace_id" in msg_event["contexts"]["trace"] + assert msg_event.type == "event" + msg_event = msg_event.payload + assert msg_event["contexts"]["trace"] + assert "trace_id" in msg_event["contexts"]["trace"] - assert error_event["contexts"]["trace"] - assert "trace_id" in error_event["contexts"]["trace"] + assert error_event.type == "event" + error_event = error_event.payload + assert error_event["contexts"]["trace"] + assert "trace_id" in error_event["contexts"]["trace"] - assert transaction_event["contexts"]["trace"] - assert "trace_id" in transaction_event["contexts"]["trace"] + assert span_item.type == "span" + span_item = span_item.payload + assert span_item["trace_id"] is not None - assert ( - msg_event["contexts"]["trace"]["trace_id"] - == error_event["contexts"]["trace"]["trace_id"] - == transaction_event["contexts"]["trace"]["trace_id"] - ) + assert ( + msg_event["contexts"]["trace"]["trace_id"] + == error_event["contexts"]["trace"]["trace_id"] + == span_item["trace_id"] + ) def test_has_trace_if_performance_disabled( @@ -371,12 +302,9 @@ def dogpark(environ, start_response): assert "trace_id" in error_event["contexts"]["trace"] -@pytest.mark.parametrize("span_streaming", [True, False]) def test_trace_from_headers_if_performance_enabled( sentry_init, - capture_events, capture_items, - span_streaming, ): def dogpark(environ, start_response): capture_message("Attempting to fetch the ball") @@ -384,15 +312,12 @@ def dogpark(environ, start_response): sentry_init( traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(dogpark) client = Client(app) - if span_streaming: - items = capture_items("event", "span") - else: - events = capture_events() + items = capture_items("event", "span") trace_id = "582b43a4192642f0b136d5159a501701" sentry_trace_header = "{}-{}-{}".format(trace_id, "6e8f22c393e68f19", 1) @@ -405,27 +330,11 @@ def dogpark(environ, start_response): sentry_sdk.flush() - if span_streaming: - msg_event, error_event, span_item = items - - assert msg_event.payload["contexts"]["trace"]["trace_id"] == trace_id - assert error_event.payload["contexts"]["trace"]["trace_id"] == trace_id - assert span_item.payload["trace_id"] == trace_id - else: - msg_event, error_event, transaction_event = events - - assert msg_event["contexts"]["trace"] - assert "trace_id" in msg_event["contexts"]["trace"] - - assert error_event["contexts"]["trace"] - assert "trace_id" in error_event["contexts"]["trace"] + msg_event, error_event, span_item = items - assert transaction_event["contexts"]["trace"] - assert "trace_id" in transaction_event["contexts"]["trace"] - - assert msg_event["contexts"]["trace"]["trace_id"] == trace_id - assert error_event["contexts"]["trace"]["trace_id"] == trace_id - assert transaction_event["contexts"]["trace"]["trace_id"] == trace_id + assert msg_event.payload["contexts"]["trace"]["trace_id"] == trace_id + assert error_event.payload["contexts"]["trace"]["trace_id"] == trace_id + assert span_item.payload["trace_id"] == trace_id def test_trace_from_headers_if_performance_disabled( @@ -461,11 +370,9 @@ def dogpark(environ, start_response): assert error_event["contexts"]["trace"]["trace_id"] == trace_id -@pytest.mark.parametrize("span_streaming", [True, False]) def test_traces_sampler_gets_correct_values_in_sampling_context( sentry_init, DictionaryContaining, # noqa:N803 - span_streaming, ): def app(environ, start_response): start_response("200 OK", []) @@ -475,7 +382,7 @@ def app(environ, start_response): sentry_init( send_default_pii=True, traces_sampler=traces_sampler, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(app) client = Client(app) @@ -501,9 +408,8 @@ def app(environ, start_response): ) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_session_mode_defaults_to_request_mode_in_wsgi_handler( - capture_envelopes, sentry_init, span_streaming + capture_envelopes, sentry_init ): """ Test that ensures that even though the default `session_mode` for @@ -519,7 +425,7 @@ def app(environ, start_response): sentry_init( send_default_pii=True, traces_sampler=traces_sampler, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(app) envelopes = capture_envelopes() @@ -543,10 +449,7 @@ def app(environ, start_response): assert aggregates[0]["exited"] == 1 -@pytest.mark.parametrize("span_streaming", [True, False]) -def test_auto_session_tracking_with_aggregates( - sentry_init, capture_envelopes, span_streaming -): +def test_auto_session_tracking_with_aggregates(sentry_init, capture_envelopes): """ Test for correct session aggregates in auto session tracking. """ @@ -562,7 +465,7 @@ def sample_app(environ, start_response): sentry_init( send_default_pii=True, traces_sampler=traces_sampler, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(sample_app) envelopes = capture_envelopes() @@ -583,10 +486,7 @@ def sample_app(environ, start_response): for item in envelope.items: count_item_types[item.type] += 1 - if span_streaming: - assert count_item_types["span"] == 3 - else: - assert count_item_types["transaction"] == 3 + assert count_item_types["span"] == 3 assert count_item_types["event"] == 1 assert count_item_types["sessions"] == 1 @@ -600,8 +500,7 @@ def sample_app(environ, start_response): assert sum(agg.get("crashed", 0) for agg in session_aggregates) == 1 -@pytest.mark.parametrize("span_streaming", [True, False]) -def test_span_origin_manual(sentry_init, capture_events, capture_items, span_streaming): +def test_span_origin_manual(sentry_init, capture_items): def dogpark(environ, start_response): start_response("200 OK", []) return ["Go get the ball! Good dog!"] @@ -609,30 +508,22 @@ def dogpark(environ, start_response): sentry_init( send_default_pii=True, traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware(dogpark) - if span_streaming: - items = capture_items("span") - else: - events = capture_events() + items = capture_items("span") client = Client(app) client.get("/dogs/are/great/") sentry_sdk.flush() - if span_streaming: - assert len(items) == 1 - assert items[0].payload["attributes"]["sentry.origin"] == "manual" - else: - (event,) = events - assert event["contexts"]["trace"]["origin"] == "manual" + assert len(items) == 1 + assert items[0].payload["attributes"]["sentry.origin"] == "manual" -@pytest.mark.parametrize("span_streaming", [True, False]) -def test_span_origin_custom(sentry_init, capture_events, capture_items, span_streaming): +def test_span_origin_custom(sentry_init, capture_items): def dogpark(environ, start_response): start_response("200 OK", []) return ["Go get the ball! Good dog!"] @@ -640,29 +531,22 @@ def dogpark(environ, start_response): sentry_init( send_default_pii=True, traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) app = SentryWsgiMiddleware( dogpark, span_origin="auto.dogpark.deluxe", ) - if span_streaming: - items = capture_items("span") - else: - events = capture_events() + items = capture_items("span") client = Client(app) client.get("/dogs/are/great/") sentry_sdk.flush() - if span_streaming: - assert len(items) == 1 - assert items[0].payload["attributes"]["sentry.origin"] == "auto.dogpark.deluxe" - else: - (event,) = events - assert event["contexts"]["trace"]["origin"] == "auto.dogpark.deluxe" + assert len(items) == 1 + assert items[0].payload["attributes"]["sentry.origin"] == "auto.dogpark.deluxe" @pytest.mark.parametrize(