Skip to content

feat: capture Litestar errors without LitestarIntegration while Sentry tracing is off - #361

Merged
lesnik512 merged 1 commit into
mainfrom
lean-litestar-capture
Oct 28, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
lean-litestar-capture

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Closes #307.

While Sentry tracing is off, lite-bootstrap now reports Litestar request errors itself, without sentry-sdk's LitestarIntegration. With tracing off, the integration still builds and discards a transaction on every request. On 3.0 it also records a real span when the request carries a sampled sentry-trace.

This is the first of three lean-capture PRs. #341 (FastAPI, FastMCP) reuses the shared pieces here, and #313 changes auto_enabling_integrations on top of both.

Changes

  • LitestarConfig.sentry_lean_error_capture: bool | None = None. The value is resolved by is_sentry_lean_error_capture_on():
    • None turns lean capture on when SentryConfig.is_sentry_tracing_requested() is false and sentry_integrations holds no LitestarIntegration.
    • True forces it, and warns as feat: add sentry_lean_error_capture for Litestar #304 did when tracing is on or a LitestarIntegration is supplied.
    • False keeps the integration.
  • SentryConfig.is_sentry_tracing_requested(). True when sentry_traces_sample_rate, or traces_sample_rate, traces_sampler or enable_tracing in sentry_additional_params, is not None. Capture Sentry errors without the Starlette integration when Sentry tracing is off (FastAPI, FastMCP) #341 needs the same rule.
  • Shared Sentry pieces from feat: add sentry_lean_error_capture for Litestar #304. SentryInstrument is generic over its config and builds its kwargs in _build_init_params(). LitestarSentryInstrument appends LitestarIntegration to disabled_integrations. LitestarSentryCaptureMiddleware and capture_litestar_exception_after_handling are ported, with these changes:
    • sessions.track_session is called directly. The auto_session_tracking_scope fallback and the is_sentry_litestar_integration_installed probe are gone, as the issue asked.
    • Litestar's logger is ignored through ignore_logger_for_events on 3.x and ignore_logger on 2.x, like the integration on each major.
    • event_from_exception gets client_options on 2.x only, because 3.x dropped the parameter.
  • Request data. It now comes from the same functions sentry-sdk uses, so it matches the integration on both majors:
    • _get_request_data from sentry_sdk.integrations._asgi_common supplies the method, URL, query string, headers, and the client address when user_info is on. It runs the headers and query string through the data_collection denylists.
    • Cookies are filtered with _apply_key_value_collection_filtering, as LitestarIntegration does.
    • The user comes from retrieve_user_from_scope, only when user_info is on.
    • A body is attached only when http_bodies includes "incoming_request" and the handler already read it. It is taken from Litestar's ScopeState: JSON first, then form, then the raw body.
  • scripts/floor_smoke.py. The Litestar target sends one failing request through the default path. I ran the litestar floors locally on 3.11 and 3.14 (sentry-sdk 2.70.0, litestar 2.22.0).

Departures from the issue

  • Private sentry-sdk helpers. _get_request_data, _RootPathInPath and _apply_key_value_collection_filtering are underscored. feat: add sentry_lean_error_capture for Litestar #304 reimplemented the header masking by hand. With data_collection always set, that would have meant copying sentry-sdk's denylist matching as well, and it would drift. The sentry-majors job runs the request-data tests on both majors, so a rename fails CI.
  • A supplied LitestarIntegration keeps the integration under None. feat: add sentry_lean_error_capture for Litestar #304 warned and dropped it, which was fine for an opt-in. With lean capture as the default, the same rule would silently override a user's LitestarIntegration(failed_request_status_codes=...).
  • enable_tracing counts as tracing. It is a 2.x option that turns tracing on, and it raises TypeError on 3.x.
  • No content_length. The integration reads scope["_body"], which Litestar 2.25 never sets, so it always reports 0.
  • No litestar-sqlalchemy-template rerun, and no Litestar rows in performance.md from Benchmark docs: add Litestar, FastStream and FastMCP tables, the unsampled-OTel floor and the macOS caveat #335, which hasn't landed. The in-process rerun is below.

Tests

tests/test_litestar_sentry_capture.py was written first. #304's tests are ported, and these are new:

  • test_lean_capture_is_the_default_while_tracing_is_off builds a LitestarConfig with only sentry_dsn set. On main it failed with 'litestar' in the active integrations.
  • Three tracing cases (sentry_traces_sample_rate, traces_sample_rate, traces_sampler) keep the integration, as does a supplied LitestarIntegration.
  • Cookies are filtered by the default denylist and dropped when cookies is off. The user and client address appear only with user_info. The body cases: JSON, form and raw bodies read by the handler, a body the handler never read, and http_bodies=[].
  • "One event per failing request" runs with service_debug off and on, on both majors.

test_litestar_bootstrapper_runs_without_sentry_sdk reloads the Litestar module with sentry-sdk missing. It caught a module-level IS_SENTRY_SDK_2 import that failed without sentry-sdk installed.

The fixture that resets litestar in sentry-sdk's process-wide registries now clears it from _processed_integrations too. Otherwise an earlier lean bootstrap leaves the integration "processed but not installed", and a later test cannot turn it on. Re-enabling it runs setup_once again and patches HTTPRoute.handle a second time. So the tests that use the integration path check only that it is active, not how many events it sends.

The full suite passes at 100% on sentry-sdk 2.73.0 and 3.0.0b2, and ty passes against both. just lint, just adr-check, just docs-build and lychee pass.

Measurement

These are in-process numbers from benchmarks/frameworks/bench.py litestar ... --repeat 2 on an M2 Mac (CPython 3.14.7, litestar 2.25.0), in µs per request over bare Litestar. The new scenarios are in litestar_suite.py.

scenario 2.73.0 3.0.0b2
sentry (lean, the new default) +7.3 +8.3
sentry_sampled_trace +9.7 +10.1
sentry_integration (sentry_lean_error_capture=False) +70.7 +59.5
sentry_integration_sampled_trace +74.4 +147.4

sentry_nointegration, which disables the integration and adds nothing else, measured +7.9 on both majors, so the lean middleware itself costs almost nothing.

Docs

  • docs/integrations/litestar.md: a new Sentry section covering the rule, what is reported, what request data is attached, and the numbers.
  • configuration.md: a bullet for the field.
  • performance.md: the "Sentry costs more on Litestar" paragraph now describes the 2.0 default.
  • benchmarks/README.md: the pre-2.0 Litestar rows are relabelled "1.x defaults", and the rerun table is added.

Breaking changes

These apply to Litestar services with a sentry_dsn and no tracing option.

  • An exception that an exception_handlers entry turns into a 4xx response is no longer reported. LitestarIntegration reports it.
  • No request transactions are created. This affects only code that reads the current transaction, since tracing was already off.
  • Events carry the request data listed above. A non-GET body is attached only when the handler read it and http_bodies allows it. The integration read it before the handler ran.

Set sentry_lean_error_capture=False to keep the integration.

…y tracing is off

LitestarConfig.sentry_lean_error_capture defaults to None, which turns lean
capture on while no Sentry tracing option is set. lite-bootstrap then disables
LitestarIntegration and reports request errors through its own middleware and
after_exception hook, with request data gated by data_collection.

SentryInstrument is generic over its config and builds init kwargs in
_build_init_params, so framework instruments can extend them.
@lesnik512
lesnik512 merged commit 02f0332 into main Oct 28, 2026
37 checks passed
@lesnik512
lesnik512 deleted the lean-litestar-capture branch October 28, 2026 18:35
lesnik512 added a commit that referenced this pull request Oct 28, 2026
…tracing is off (#362)

* Report FastAPI and FastMCP errors without StarletteIntegration while tracing is off

While Sentry tracing is off, FastAPIBootstrapper and FastMcpBootstrapper disable
sentry-sdk's StarletteIntegration (and FastApiIntegration on FastAPI) and report
request errors through a pure-ASGI middleware instead, with the same
sentry_lean_error_capture rule Litestar got in #361. The rule now lives in
SentryLeanCaptureConfig, shared by all three configs.

Closes #341.

* Stop the integration reset fixture from re-patching Starlette in every test

Clearing sentry-sdk's processed-integrations registry before each test made
StarletteIntegration.setup_once run again for every bootstrap, and each run
wraps Starlette.__call__ once more. By the end of the suite 126 wrappers were
stacked, enough to overflow the stack on Python 3.11 in CI. Once an
integration has been set up, the fixture now replaces its setup_once with a
no-op, so the registries still read as a fresh process without patching twice.

* Keep the exploding test handler from failing a later test's logging shutdown

test_teardown_removes_filters_even_when_a_handler_close_fails added a handler
whose close always raised and left it on the root logger. Its exception kept
it alive until the cyclic garbage collector ran, so whether Litestar's
dictConfig in a later test closed it again, and failed, depended on GC timing.
The handler now raises on its first close only, and the test detaches it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Capture Sentry errors without LitestarIntegration when Sentry tracing is off

1 participant