Skip to content

feat!: trace Litestar through its own OpenTelemetryPlugin - #279

Merged
lesnik512 merged 1 commit into
mainfrom
feat/litestar-opentelemetry-plugin
Oct 9, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
feat/litestar-opentelemetry-plugin

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Why

Litestar ships OpenTelemetryPlugin since 2.22. This replaces LitestarOpenTelemetryInstrumentationMiddleware, our hand-rolled wrapper around OpenTelemetryMiddleware with its WeakKeyDictionary cache of per-next_app instances, so we stop maintaining it. Ported from community-of-python/microbootstrap#154.

What changes

  • LitestarOpenTelemetryInstrument appends OpenTelemetryPlugin to application_config.plugins with the installed tracer and meter providers. User plugins are kept.
  • The plugin wraps the whole application, so its span starts before routing, when path_template is unknown. A small LitestarOpenTelemetryRouteMiddleware runs inside the route stack and renames the current server span to GET /users/{user_id} and sets http.route. This is the only custom piece left, and it holds no state.
  • Exclusions go to the plugin's exclude, which Litestar searches in scope["path"]. Derived paths (metrics, and health when opentelemetry_generate_health_check_spans=False) are anchored as ^/path(?:/|$), so /custom-health/ excludes both slash forms but not /custom-healthy. An empty list is passed as None, because Litestar compiles [] into a pattern that skips every path.
  • is_litestar_opentelemetry_installed also requires litestar.plugins.opentelemetry. With Litestar < 2.22 plus OpenTelemetry installed, the instrument is now skipped with a dependency warning instead of failing at import lite_bootstrap.

Breaking

  • The litestar-otl extra requires litestar>=2.22 (the litestar extra stays at 2.19).
  • opentelemetry_excluded_urls entries are searched in the request path instead of the full URL. Path fragments such as /items/\d+$ behave the same; a pattern anchored on scheme or host (^https?://...) no longer matches.
  • Requests to unmatched paths used to produce no server span at all, because the old middleware sat inside the route stack. They now produce a span named after the method alone (GET), without http.route, so scanners cannot inflate span-name cardinality.
  • OTEL_PYTHON_LITESTAR_EXCLUDED_URLS is now honored, through the plugin's exclude_urls_env_key.

Tests

  • Exported spans carry the route template, unmatched paths get a method-only span, user plugins are kept, the env exclusion works, nothing is excluded when there is nothing to exclude, and the instrument is skipped without litestar.plugins.opentelemetry.
  • The tests for the removed middleware's cache and its parsed exclude list are gone.
  • just lint and just test pass (343 passed).
  • Floor smoke (litestar==2.22.0, opentelemetry-instrumentation-asgi==0.49b0, Python 3.11) passes. At those floors, real requests produce GET /items/{item_id}, GET for a 404 and GET /h for the health route, and /metrics is excluded.

LitestarOpenTelemetryInstrument registers litestar.plugins.opentelemetry's
OpenTelemetryPlugin instead of hand-wrapping OpenTelemetryMiddleware in a cached
ASGI middleware. A small route middleware renames the server span to the route
template, which the plugin cannot see because it runs before routing.

Breaking: the litestar-otl extra needs litestar>=2.22; opentelemetry_excluded_urls
are searched in the request path rather than the full URL; unmatched paths now
produce a method-only span; OTEL_PYTHON_LITESTAR_EXCLUDED_URLS is honored.
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