Repository navigation
feat(fastapi): Capture request body on segment span under streaming - #6136
Conversation
Add span-streaming support to the Starlette integration so middleware spans and active-thread tracking work under the `trace_lifecycle: "stream"` experiment, while preserving the legacy transaction-based behavior. When span streaming is enabled, `_enable_span_for_middleware` starts middleware spans via `sentry_sdk.traces.start_span` with `sentry.op`, `sentry.origin`, and `starlette.middleware_name` attributes instead of the legacy `start_span(op=..., origin=...)` + tag pattern. In `patch_request_response`, when the current scope holds a `StreamedSpan` (and not a `NoOpStreamedSpan`), the profiler hook now calls `_segment._update_active_thread()`; otherwise the legacy `current_scope.transaction.update_active_thread()` path is preserved. Tests are parametrized across streaming and static modes for `test_middleware_spans`, `test_middleware_spans_disabled`, `test_middleware_callback_spans`, and `test_span_origin`. A new `test_active_thread_id_span_streaming` verifies the segment's `thread.id` attribute under streaming. `auto_enabling_integrations` is disabled in tests where auto-instrumented spans would leak into the captured span stream. Refs PY-2362 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When span streaming is enabled, attach the request body (serialized via a JSON default that unwraps AnnotatedValue) to the active segment span as `http.request.body.data` inside `patch_request_response`. This mirrors the body capture that the legacy request-event processor does, so streamed spans carry the same payload context without relying on transaction events. PII scrubbing is intentionally not applied here — under span streaming, sanitization is expected to happen in `before_send_span` hooks. Tests cover the unscrubbed passthrough, a top-level AnnotatedValue (raw bytes), and a nested AnnotatedValue inside a multipart form upload. Refs PY-2362 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ontent arg which only happens once httpx is part of starlette
Under span streaming with middleware_spans=True, sentry_sdk.get_current_span() returns the innermost middleware span rather than the segment, so http.request.body.data was being attached to a middleware child span. Route the attribute through current_span._segment so it always lands on the segment, mirroring the _update_active_thread pattern in _sentry_sync_func. Also deduplicate the sentry_sdk.get_client() call in _sentry_async_func, and parametrize the three body-data streaming tests over middleware_spans=[False, True]. Refs PY-2362 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename the `starlette.middleware_name` span attribute to `middleware.name` on middleware spans emitted under span streaming. The non-streaming tag remains unchanged. Update the affected test assertions accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FastAPI runs its own request-body extraction and was missing the same logic as that which exists in the Starlette integration. Extract the logic that writes `http.request.body.data` on the streaming segment into a shared helper in the Starlette integration, and call it from FastAPI's request handler once the body has been read.
Codecov Results 📊✅ 142 passed | Total: 142 | Pass Rate: 100% | Execution Time: 21.48s All tests are passing successfully. ❌ Patch coverage is 7.69%. Project has 13975 uncovered lines. Files with missing lines (2)
Generated by Codecov Action |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 793e2f7. Configure here.
FastAPI 0.79.1 pulls in Starlette 0.19.1, whose `TestClient` is built on `requests` and does not accept the `content` kwarg. Match the skipif already in place for the analogous Starlette test so CI on older FastAPI versions passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d do not set the request.body.data attribute in that scenario
…to-span-first-rme79
…to-span-first-rme79
There was a problem hiding this comment.
We should def make sure to call out the change in automatic sanitization and let folks know they'll need to sanitize themselves. Added it to https://linear.app/getsentry/project/span-first-sdk-python-727da28dd037/overview -- we can keep a running list there so that we don't forget about these.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b9c3221. Configure here.
) Removes `_serialize_request_body_data` from `sentry_sdk/integrations/starlette.py`. Nothing calls it. It was added in #6136 to serialize the request body for the segment span under span streaming. #6286 changed the FastAPI path to serialize the cached `request._json` / `request._form` directly and removed the only call site (`_serialize_request_body_data(info["data"])`). The definition stayed behind. Why it's safe: - The underscore-prefixed name doesn't appear anywhere else in the repo: no references in `sentry_sdk/`, `tests/`, `scripts/`, or docs. - None of the open PRs use it. - The module still uses the `json` and `AnnotatedValue` imports elsewhere, so this change doesn't remove any imports. - Users see no change in behavior or public API. <!-- junior-request-attribution:start --> via [automation `sched_65eed06cc199036c2b13411096f412d4`](https://junior-prod.sentry.dev/automations/sched_65eed06cc199036c2b13411096f412d4) · Run ID `sched_65eed06cc199036c2b13411096f412d4:1793267486380`. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:agent-dispatch%3Adispatch_d52fabcfb189e56dc472324ad6411165 --> -- [View Junior Session](https://junior-prod.sentry.dev/conversations/agent-dispatch%3Adispatch_d52fabcfb189e56dc472324ad6411165) [[Sentry]](https://sentry.sentry.io/explore/conversations/agent-dispatch%3Adispatch_d52fabcfb189e56dc472324ad6411165/?project=4510944073809921) <!-- junior-session-footer:end --> Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>

Capture the request body as
http.request.body.dataon the streaming segment span for FastAPI requests, matching the behavior already in place for Starlette.FastAPI runs its own request-body extraction path, so it was bypassing the data-capture logic that lives in the Starlette integration. This extracts that logic into a shared helper in the Starlette integration and invokes it from FastAPI's request handler once the body has been read.
Stacked on top of #6123.
Refs PY-2322
Fixes #6020