Skip to content

Commit 02f0332

Browse files
authored
Report Litestar errors without LitestarIntegration while tracing is off (#361)
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.
1 parent 06c3ec1 commit 02f0332

10 files changed

Lines changed: 833 additions & 14 deletions

File tree

‎benchmarks/README.md‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ prometheus-client 0.26.0, structlog 26.1.0. In-process, baseline 12.1 µs per re
368368
| OTel 0% + OTel HTTP metrics | +73.6 | +12.9 over OTel 0%, second run |
369369
| OTel 0% + Prometheus | +76.5 | +15.8 over OTel 0%, second run |
370370
| OTel 0% + OTel HTTP metrics + Prometheus | +90.2 | +29.5 over OTel 0%, second run |
371-
| Sentry, lite-bootstrap defaults (tracing off) | +70.9 | LitestarIntegration |
371+
| Sentry, lite-bootstrap 1.x defaults (tracing off) | +70.9 | LitestarIntegration |
372372
| Sentry, `sentry_auto_session_tracking=False` | +63.7 | sessions cost 7.2 |
373373
| Sentry, `auto_enabling_integrations=False` | -0.1 | LitestarIntegration is auto-enabling, so nothing captures request errors |
374374
| Sentry, `sentry_default_integrations=False` | -0.2 | same reason |
@@ -386,6 +386,21 @@ prometheus-client 0.26.0, structlog 26.1.0. In-process, baseline 12.1 µs per re
386386
The "second run" rows come from a noisier re-check (spread between rounds up to 67% per row). The
387387
first run's OTel 0% row is +56.8, the re-check's +60.7, and the deltas in the notes are against the
388388
re-check.
389+
Since 2.0, lite-bootstrap reports Litestar request errors itself while Sentry tracing is off, without
390+
`LitestarIntegration`. Rerun in-process on the M2 host (macOS, CPython 3.14.7, litestar 2.25.0,
391+
`bench.py litestar bare,sentry,sentry_sampled_trace,sentry_integration,sentry_integration_sampled_trace --repeat 2`):
392+
393+
| scenario | sentry-sdk 2.73.0 | 3.0.0b2 |
394+
|---|---:|---:|
395+
| `sentry`: lean capture, the 2.0 default | +7.3 | +8.3 |
396+
| `sentry_sampled_trace`: the same, request carries a sampled `sentry-trace` | +9.7 | +10.1 |
397+
| `sentry_integration`: `sentry_lean_error_capture=False` | +70.7 | +59.5 |
398+
| `sentry_integration_sampled_trace` | +74.4 | +147.4 |
399+
400+
On 3.0 an incoming sampled `sentry-trace` makes `LitestarIntegration` record a real span even with
401+
tracing off. The table above was measured before 2.0. Its `sentry_nosess` scenario now has the same
402+
config as `sentry`, because sessions are off by default since 2.0.
403+
389404
lite-bootstrap's Litestar defaults with no endpoint configured (Prometheus, health, logging and
390405
Swagger on) cost +11.7 µs on `/ping`, all of it Prometheus. The access-log middleware writes each record
391406
twice because Litestar's own stdlib logging config is applied at startup next to lite-bootstrap's.
@@ -407,8 +422,8 @@ prometheus-client 0.26.0, structlog 26.1.0. Baseline 33350 RPS, 30.0 µs per req
407422
| OpenTelemetry, 10%, exclude receive/send | 11258 | +58.8 |
408423
| Prometheus | 18823 | +23.1 |
409424
| Litestar access log on | 6879 | +115.4 |
410-
| Sentry, defaults | 8119 | +93.2 |
411-
| Sentry, sessions off | 9184 | +78.9 |
425+
| Sentry, 1.x defaults | 8119 | +93.2 |
426+
| Sentry, 1.x defaults, sessions off | 9184 | +78.9 |
412427
| CORS (ab sends no Origin) | 30942 | +2.3 |
413428
| health check | 28337 | +5.3 |
414429
| health check with OpenTelemetry spans | 4600 | +187.4 |

‎benchmarks/frameworks/litestar_suite.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
HEALTH: typing.Final = {"health_checks_enabled": True}
3939
LITESTAR_DEFAULTS: typing.Final = {**PROM, **HEALTH, **LOG, "swagger_path": "/docs"}
4040
FULL: typing.Final = {**OTEL, **PROM, **LOG, **sentry(), **HEALTH}
41+
SAMPLED_SENTRY_TRACE: typing.Final = ((b"sentry-trace", b"771a43a4192642f0b136d5159a501700-1234567890abcdef-1"),)
4142

4243
SCENARIOS: typing.Final = {
4344
"bare": Scenario(config=base_config()),
@@ -63,6 +64,11 @@
6364
"sentry_noauto": Scenario(config=base_config(**sentry(auto_enabling_integrations=False))),
6465
"sentry_nodefault": Scenario(config=base_config(**sentry(), sentry_default_integrations=False)),
6566
"sentry_nointegration": Scenario(config=base_config(**sentry(disabled_integrations=[LitestarIntegration]))),
67+
"sentry_integration": Scenario(config=base_config(**sentry(), sentry_lean_error_capture=False)),
68+
"sentry_sampled_trace": Scenario(config=base_config(**sentry()), headers=SAMPLED_SENTRY_TRACE),
69+
"sentry_integration_sampled_trace": Scenario(
70+
config=base_config(**sentry(), sentry_lean_error_capture=False), headers=SAMPLED_SENTRY_TRACE
71+
),
6672
"cors_noorigin": Scenario(config=base_config(**CORS)),
6773
"cors": Scenario(config=base_config(**CORS), headers=ORIGIN),
6874
"bare_origin": Scenario(config=base_config(), headers=ORIGIN),

‎docs/integrations/litestar.md‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,53 @@ A bare `LoggingMiddlewareConfig()` restores all of Litestar's own defaults,
106106
including full request/response body logging, so pass explicit
107107
`request_log_fields` / `response_log_fields`.
108108

109+
## Sentry
110+
111+
While Sentry tracing is off, lite-bootstrap reports request errors itself instead of through
112+
sentry-sdk's `LitestarIntegration`. Tracing counts as off when `sentry_traces_sample_rate` is unset
113+
and `sentry_additional_params` sets none of `traces_sample_rate`, `traces_sampler` and
114+
`enable_tracing`. With tracing off, the integration still builds a transaction and a span per
115+
middleware for every request and then discards them
116+
([getsentry/sentry-python#7400](https://github.com/getsentry/sentry-python/issues/7400)). On
117+
sentry-sdk 3.0, an incoming `sentry-trace` header marked as sampled makes it record a real span.
118+
119+
`sentry_lean_error_capture` picks the path:
120+
121+
- `None`, the default: lean capture while tracing is off and `sentry_integrations` holds no
122+
`LitestarIntegration`, the integration otherwise.
123+
- `True`: lean capture always. lite-bootstrap warns when tracing is on, since no request starts a
124+
transaction, and when `sentry_integrations` holds a `LitestarIntegration`, which gets dropped.
125+
- `False`: the integration always.
126+
127+
Lean capture passes `LitestarIntegration` in `disabled_integrations` and adds a middleware and an
128+
`after_exception` hook. The middleware gives each request its own Sentry scope, and its own
129+
release-health session when `sentry_auto_session_tracking` is on. It continues an incoming
130+
`sentry-trace` and `baggage`, and attaches the request to every event sent while the request runs.
131+
132+
The request data follows the same [`data_collection`](../introduction/configuration.md#sentry-data-collection)
133+
settings the integration reads. Method, URL, query string, headers and cookies are filtered by
134+
their denylists. The client address and `scope["user"]` are attached only when `user_info` is on.
135+
A body is attached only when `http_bodies` includes `"incoming_request"` and the handler has
136+
already read it, because lean capture never reads the stream itself. The integration reads every
137+
non-GET body before the handler runs.
138+
139+
An exception is reported only when its response is a 5xx, so one that your `exception_handlers`
140+
answer with a 404 is not reported. The integration reports every exception except an
141+
`HTTPException` below 500. Litestar logs the exceptions it handles, and lean capture keeps the
142+
`litestar` logger from turning them into a second event, as the integration does.
143+
144+
Measured in-process on a do-nothing handler, over the same app without Sentry:
145+
146+
| Sentry setup | sentry-sdk 2.73.0 | 3.0.0b2 |
147+
|---|---:|---:|
148+
| lean capture, the default | +7.3 µs | +8.3 µs |
149+
| `sentry_lean_error_capture=False` | +70.7 µs | +59.5 µs |
150+
| the same, request carries a sampled `sentry-trace` | +74.4 µs | +147.4 µs |
151+
152+
sentry-sdk records installed integrations for the whole process, and `disabled_integrations`
153+
cannot remove one that an earlier `sentry_sdk.init` installed. If that happened, lite-bootstrap
154+
warns that errors may be reported twice.
155+
109156
## Prometheus
110157

111158
`prometheus_group_path` defaults to `True`, so the `path` metric label uses the

‎docs/introduction/configuration.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Additional parameters can also be supplied through the settings object:
2222
- `sentry_additional_params` - additional params, which will be passed to `sentry_sdk.init`, overriding any of the above that map to the same keyword
2323
- `sentry_default_integrations` - whether to use sentry's default integrations (default: `True`)
2424
- `sentry_before_send` - optional callback chained after the built-in structlog enricher, passed to `sentry_sdk.init(before_send=...)`
25+
- `sentry_lean_error_capture` - Litestar only: report request errors without Sentry's `LitestarIntegration` (default: `None`, meaning on while Sentry tracing is off). See [Litestar](../integrations/litestar.md#sentry).
2526

2627
Read more about sentry_sdk params [here](https://docs.sentry.io/platforms/python/configuration/options/).
2728

‎docs/introduction/performance.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,12 @@ The same app on Granian, over real sockets:
104104

105105
--8<-- "benchmarks/README.md:litestargranian"
106106

107-
**Sentry costs more on Litestar than on FastAPI**, and all of it is `LitestarIntegration`: turning
108-
off auto-enabling integrations removes the cost and, with it, request error capture. The access log
109-
([`logging_access_log_enabled`](../integrations/litestar.md#logging)) is the other large opt-in
110-
item, at about 70 µs per request in-process.
107+
**Before 2.0, Sentry cost more on Litestar than on FastAPI**, and all of it was `LitestarIntegration`.
108+
Since 2.0, lite-bootstrap reports Litestar request errors itself while Sentry tracing is off, which
109+
brings Sentry from about +71 to about +7 µs per request in-process
110+
([Litestar: Sentry](../integrations/litestar.md#sentry)). The access log
111+
([`logging_access_log_enabled`](../integrations/litestar.md#logging)) is the largest opt-in item
112+
left, at about 70 µs per request in-process.
111113

112114
## FastStream
113115

0 commit comments

Comments
 (0)