Skip to content

feat: add sentry_lean_error_capture for Litestar - #304

Closed
lesnik512 wants to merge 1 commit into
mainfrom
lean-litestar-sentry-capture
Closed

lesnik512 wants to merge 1 commit into
mainfrom
lean-litestar-sentry-capture

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Adds an opt-in LitestarConfig.sentry_lean_error_capture. When on, lite-bootstrap disables sentry-sdk's LitestarIntegration and reports request errors through a middleware and an after_exception hook of its own.

Why

With traces_sample_rate unset, LitestarIntegration still builds a Transaction and a span per middleware on every request and throws them away. getsentry/sentry-python#7400 (closed as not planned, since the next major drops transactions) measured this on FastAPI. On litestar-sqlalchemy-template it is about two thirds of Sentry's per-request cost. Measured with one Granian worker on 2 CPUs, Postgres 17 and oha at 32 connections. Each cell is the median of 3 × 10 s runs, with two rounds averaged:

Sentry setup list decks (rps) deck with 20 cards (rps) added per request
none 1367 1565
LitestarIntegration (default) 929 1043 320-345 µs
sentry_lean_error_capture=True 1125 1254 about 157 µs
lean and sentry_default_integrations=False 1229 1489 32-83 µs

What lean capture still costs is mostly Sentry's other default integrations: SQLAlchemy and asyncpg breadcrumbs, logging and the rest.

What it does

  • LitestarSentryCaptureMiddleware, inserted first in application_config.middleware, runs each HTTP request in its own isolation scope and release-health session. It continues an incoming sentry-trace/baggage and attaches the request (method, URL, query string, headers) and an endpoint-style transaction name to every event sent during the request. Without send_default_pii, the headers sentry-sdk's ASGI integration masks are masked here too.
  • after_exception runs before Litestar's exception handler builds the response, so inside the middleware the hook only records the exception. The middleware reports it when http.response.start carries a 5xx. An exception that exception_handlers answer with a 404 is not reported. LitestarIntegration reports it, because it only skips HTTPExceptions below 500. Outside the middleware, the hook falls back to that same HTTPException rule. That covers routing errors and an exception handler that itself raises.
  • ignore_logger("litestar"), as LitestarIntegration does, so Litestar's own exception log does not produce a second event.
  • SentryInstrument is now generic over its config, and init kwargs are built in _build_init_params(), so LitestarSentryInstrument can append LitestarIntegration to disabled_integrations. That appends to any disabled_integrations the user passes in sentry_additional_params.
  • Config warnings for sentry_traces_sample_rate set alongside lean capture (no request transactions are started) and for a LitestarIntegration in sentry_integrations (it gets dropped).

Lean capture gives up Sentry performance monitoring for requests, and the user details LitestarIntegration adds with send_default_pii.

Floors

LitestarIntegration appeared in sentry-sdk 2.13, after disabled_integrations (2.11). The new import_checker.is_sentry_litestar_integration_installed probe therefore also tells whether that option exists. At the 2.1 floor nothing is disabled. track_session is new; 2.1 only has auto_session_tracking_scope, which takes the same arguments. generate_propagation_context, isolation_scope, event_from_exception and transaction_from_function exist at 2.1.

scripts/floor_smoke.py litestar now sends one failing request through lean capture before the default bootstrap. I ran it locally at the 3.11 floors (sentry-sdk 2.1.0, litestar 2.22.0) and the 3.14 floors (sentry-sdk 2.59.0, where LitestarIntegration ended up inactive).

A sentry-sdk quirk the tests work around

sentry-sdk records installed integrations for the whole process, and disabled_integrations cannot remove one that an earlier sentry_sdk.init installed. That only matters when one process calls init twice. Lean capture warns in that case, because errors could then be reported twice. The new test module resets just the Litestar entry around each test, so it passes in any order.

Tests

tests/test_litestar_sentry_capture.py, written first. It covers:

  • request data and header masking, with and without PII;
  • skipping mapped 4xx and client HTTPExceptions, and reporting mapped 5xx and server HTTPExceptions;
  • an exception handler that raises;
  • breadcrumb isolation between requests;
  • one event when Litestar logs the exception in debug mode;
  • continuing an incoming trace;
  • the integration swap and init kwargs;
  • the config warnings.

The full suite passes with 100% coverage. Ruff is clean. mkdocs build --strict passes.

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.

1 participant