Skip to content

Commit 9147b3c

Browse files
authored
fix(starlette): Set transaction name on current scope in sync handler (#7201)
The sync request/response handler passed the _isolation_ scope to `_set_transaction_name_and_source`, but the transaction/segment span lives on the _current_ scope. As a result the route-resolved name never reached the span for sync endpoints, which were instead named by the raw URL from the ASGI middleware (`transaction_info.source` of `url` rather than `route`). Async handlers already used the current scope and were unaffected. Pass the current scope (already computed above) so sync and async handlers behave identically: - streaming: the segment name / `sentry.segment.name.source` are route-based - static: the transaction event name / source are route-based For parametrized routes this also removes high-cardinality URL transaction names for sync endpoints. Found while working on #7183.
1 parent fe54d85 commit 9147b3c

2 files changed

Lines changed: 46 additions & 1 deletion

File tree

sentry_sdk/integrations/starlette.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any":
632632
request = args[0]
633633

634634
_set_transaction_name_and_source(
635-
sentry_scope, integration.transaction_style, request
635+
current_scope, integration.transaction_style, request
636636
)
637637

638638
extractor = StarletteRequestExtractor(request)

tests/integrations/starlette/test_starlette.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,51 @@ def test_active_thread_id_span_streaming(sentry_init, capture_items, endpoint):
14991499
assert str(data["active"]) == segments[0]["attributes"]["thread.id"]
15001500

15011501

1502+
@pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"])
1503+
def test_segment_name_is_route_resolved_name_span_streaming(
1504+
sentry_init, capture_items, endpoint
1505+
):
1506+
sentry_init(
1507+
auto_enabling_integrations=False,
1508+
integrations=[StarletteIntegration(transaction_style="url")],
1509+
traces_sample_rate=1.0,
1510+
trace_lifecycle="stream",
1511+
)
1512+
app = starlette_app_factory()
1513+
1514+
items = capture_items("span")
1515+
1516+
client = TestClient(app)
1517+
response = client.get(endpoint)
1518+
assert response.status_code == 200
1519+
1520+
sentry_sdk.flush()
1521+
1522+
segments = [item.payload for item in items if item.payload.get("is_segment")]
1523+
assert len(segments) == 1
1524+
assert segments[0]["name"] == endpoint
1525+
assert segments[0]["attributes"]["sentry.segment.name.source"] == "route"
1526+
1527+
1528+
@pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"])
1529+
def test_transaction_name_is_route_resolved_name_static(
1530+
sentry_init, capture_events, endpoint
1531+
):
1532+
sentry_init(
1533+
integrations=[StarletteIntegration(transaction_style="url")],
1534+
traces_sample_rate=1.0,
1535+
)
1536+
events = capture_events()
1537+
1538+
client = TestClient(starlette_app_factory())
1539+
response = client.get(endpoint)
1540+
assert response.status_code == 200
1541+
1542+
(transaction,) = [e for e in events if e.get("type") == "transaction"]
1543+
assert transaction["transaction"] == endpoint
1544+
assert transaction["transaction_info"] == {"source": "route"}
1545+
1546+
15021547
def test_original_request_not_scrubbed(sentry_init, capture_events):
15031548
sentry_init(integrations=[StarletteIntegration()])
15041549

0 commit comments

Comments
 (0)