From 8992d0a0fa2209dd9a982cde0f7b49cf5a3cae83 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 2 Sep 2026 14:58:14 +0200 Subject: [PATCH 1/3] feat(falcon): Add http.route attribute --- sentry_sdk/integrations/falcon.py | 5 ++ sentry_sdk/integrations/wsgi.py | 1 + tests/integrations/falcon/test_falcon.py | 71 +++++++++++++++++++++--- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/sentry_sdk/integrations/falcon.py b/sentry_sdk/integrations/falcon.py index 7a595bcf2a..7f360544ba 100644 --- a/sentry_sdk/integrations/falcon.py +++ b/sentry_sdk/integrations/falcon.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING import sentry_sdk +from sentry_sdk.consts import SPANDATA from sentry_sdk.integrations import DidNotEnable, Integration, _check_minimum_version from sentry_sdk.integrations._wsgi_common import RequestExtractor from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware @@ -116,6 +117,10 @@ def process_resource( if integration is None or not has_span_streaming_enabled(client.options): return + server_span = sentry_sdk.get_current_scope()._server_segment_span + if server_span is not None: + server_span.set_attribute(SPANDATA.HTTP_ROUTE, req.uri_template) + name_for_style = { "uri_template": req.uri_template, "path": req.path, diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index b0156a9829..3204e15e43 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -159,6 +159,7 @@ def __call__( }, parent_span=None, ) + scope.get_current_scope()._server_segment_span = span_ctx else: transaction = continue_trace( environ, diff --git a/tests/integrations/falcon/test_falcon.py b/tests/integrations/falcon/test_falcon.py index 8bf22707e1..8dc10abffe 100644 --- a/tests/integrations/falcon/test_falcon.py +++ b/tests/integrations/falcon/test_falcon.py @@ -98,27 +98,24 @@ def test_has_context( @pytest.mark.parametrize( - "url,transaction_style,expected_transaction,expected_source", + "url,expected_transaction,expected_source", [ - ("/message", "uri_template", "/message", "route"), - ("/message", "path", "/message", "url"), - ("/message/123456", "uri_template", "/message/{message_id:int}", "route"), - ("/message/123456", "path", "/message/123456", "url"), + ("/message", "/message", "route"), + ("/message/123456", "/message/{message_id:int}", "route"), ], ) @pytest.mark.parametrize("span_streaming", [True, False]) -def test_transaction_style( +def test_transaction_style_uri_template( sentry_init, make_client, capture_events, capture_items, url, - transaction_style, expected_transaction, expected_source, span_streaming, ): - integration = FalconIntegration(transaction_style=transaction_style) + integration = FalconIntegration(transaction_style="uri_template") sentry_init( integrations=[integration], traces_sample_rate=1.0, @@ -140,6 +137,64 @@ def test_transaction_style( spans = [span for span in spans if span["name"] == expected_transaction] assert len(spans) == 1 assert spans[0]["attributes"]["sentry.segment.name.source"] == expected_source + assert spans[0]["attributes"]["http.route"] == expected_transaction + else: + events = capture_events() + + response = client.simulate_get(url) + assert response.status == falcon.HTTP_200 + + (event, transaction) = events + + assert transaction["transaction"] == expected_transaction + assert transaction["transaction_info"] == {"source": expected_source} + + assert event["transaction"] == expected_transaction + assert event["transaction_info"] == {"source": expected_source} + + +@pytest.mark.parametrize( + "url,expected_transaction,expected_source,expected_route", + [ + ("/message", "/message", "url", "/message"), + ("/message/123456", "/message/123456", "url", "/message/{message_id:int}"), + ], +) +@pytest.mark.parametrize("span_streaming", [True, False]) +def test_http_route( + sentry_init, + make_client, + capture_events, + capture_items, + url, + expected_transaction, + expected_source, + expected_route, + span_streaming, +): + integration = FalconIntegration(transaction_style="path") + sentry_init( + integrations=[integration], + traces_sample_rate=1.0, + trace_lifecycle="stream" if span_streaming else "static", + ) + + client = make_client() + if span_streaming: + items = capture_items("event") + items = capture_items("event", "span") + + response = client.simulate_get(url) + assert response.status == falcon.HTTP_200 + + (event,) = (item.payload for item in items if item.type == "event") + + sentry_sdk.flush() + spans = [item.payload for item in items if item.type == "span"] + spans = [span for span in spans if span["name"] == expected_transaction] + assert len(spans) == 1 + assert spans[0]["attributes"]["sentry.segment.name.source"] == expected_source + assert spans[0]["attributes"]["http.route"] == expected_route else: events = capture_events() From c39e23cde9a4bda3d9fc551c96509c6f3788fcab Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 2 Sep 2026 15:02:22 +0200 Subject: [PATCH 2/3] add new tests instead --- tests/integrations/falcon/test_falcon.py | 64 ++++++------------------ 1 file changed, 15 insertions(+), 49 deletions(-) diff --git a/tests/integrations/falcon/test_falcon.py b/tests/integrations/falcon/test_falcon.py index 8dc10abffe..401caa82c9 100644 --- a/tests/integrations/falcon/test_falcon.py +++ b/tests/integrations/falcon/test_falcon.py @@ -98,24 +98,27 @@ def test_has_context( @pytest.mark.parametrize( - "url,expected_transaction,expected_source", + "url,transaction_style,expected_transaction,expected_source", [ - ("/message", "/message", "route"), - ("/message/123456", "/message/{message_id:int}", "route"), + ("/message", "uri_template", "/message", "route"), + ("/message", "path", "/message", "url"), + ("/message/123456", "uri_template", "/message/{message_id:int}", "route"), + ("/message/123456", "path", "/message/123456", "url"), ], ) @pytest.mark.parametrize("span_streaming", [True, False]) -def test_transaction_style_uri_template( +def test_transaction_style( sentry_init, make_client, capture_events, capture_items, url, + transaction_style, expected_transaction, expected_source, span_streaming, ): - integration = FalconIntegration(transaction_style="uri_template") + integration = FalconIntegration(transaction_style=transaction_style) sentry_init( integrations=[integration], traces_sample_rate=1.0, @@ -137,7 +140,6 @@ def test_transaction_style_uri_template( spans = [span for span in spans if span["name"] == expected_transaction] assert len(spans) == 1 assert spans[0]["attributes"]["sentry.segment.name.source"] == expected_source - assert spans[0]["attributes"]["http.route"] == expected_transaction else: events = capture_events() @@ -153,61 +155,25 @@ def test_transaction_style_uri_template( assert event["transaction_info"] == {"source": expected_source} -@pytest.mark.parametrize( - "url,expected_transaction,expected_source,expected_route", - [ - ("/message", "/message", "url", "/message"), - ("/message/123456", "/message/123456", "url", "/message/{message_id:int}"), - ], -) -@pytest.mark.parametrize("span_streaming", [True, False]) def test_http_route( sentry_init, make_client, - capture_events, capture_items, - url, - expected_transaction, - expected_source, - expected_route, - span_streaming, ): - integration = FalconIntegration(transaction_style="path") sentry_init( - integrations=[integration], + integrations=[FalconIntegration()], traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) client = make_client() - if span_streaming: - items = capture_items("event") - items = capture_items("event", "span") - - response = client.simulate_get(url) - assert response.status == falcon.HTTP_200 + items = capture_items("span") - (event,) = (item.payload for item in items if item.type == "event") - - sentry_sdk.flush() - spans = [item.payload for item in items if item.type == "span"] - spans = [span for span in spans if span["name"] == expected_transaction] - assert len(spans) == 1 - assert spans[0]["attributes"]["sentry.segment.name.source"] == expected_source - assert spans[0]["attributes"]["http.route"] == expected_route - else: - events = capture_events() + client.simulate_get("/message/123456") - response = client.simulate_get(url) - assert response.status == falcon.HTTP_200 - - (event, transaction) = events - - assert transaction["transaction"] == expected_transaction - assert transaction["transaction_info"] == {"source": expected_source} - - assert event["transaction"] == expected_transaction - assert event["transaction_info"] == {"source": expected_source} + sentry_sdk.flush() + (segment,) = [item.payload for item in items if item.payload.get("is_segment")] + assert segment["attributes"]["http.route"] == "/message/{message_id:int}" @pytest.mark.parametrize("span_streaming", [True, False]) From ea6cc603bae3533d27e7f965044a052beee3f212 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 2 Sep 2026 15:28:24 +0200 Subject: [PATCH 3/3] use SPANDATA const in test --- tests/integrations/falcon/test_falcon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integrations/falcon/test_falcon.py b/tests/integrations/falcon/test_falcon.py index 401caa82c9..79765bc537 100644 --- a/tests/integrations/falcon/test_falcon.py +++ b/tests/integrations/falcon/test_falcon.py @@ -5,6 +5,7 @@ import pytest import sentry_sdk +from sentry_sdk.consts import SPANDATA from sentry_sdk.integrations.falcon import FalconIntegration from sentry_sdk.integrations.logging import LoggingIntegration from sentry_sdk.utils import parse_version @@ -173,7 +174,7 @@ def test_http_route( sentry_sdk.flush() (segment,) = [item.payload for item in items if item.payload.get("is_segment")] - assert segment["attributes"]["http.route"] == "/message/{message_id:int}" + assert segment["attributes"][SPANDATA.HTTP_ROUTE] == "/message/{message_id:int}" @pytest.mark.parametrize("span_streaming", [True, False])