Skip to content

refactor(middleware): migrate PrometheusMiddleware to ASGIMiddleware - #5006

Open
Kumzy wants to merge 1 commit into
mw/rate-limitfrom
mw/prometheus
Open

refactor(middleware): migrate PrometheusMiddleware to ASGIMiddleware#5006
Kumzy wants to merge 1 commit into
mw/rate-limitfrom
mw/prometheus

Conversation

@Kumzy

@Kumzy Kumzy commented Aug 18, 2026

Copy link
Copy Markdown
Member

Part of #4009. Stacked PR to avoid conflicts in the changelog and whats-new docs.

Moving PrometheusMiddleware from AbstractMiddleware to ASGIMiddleware.

The constructor takes keyword arguments now instead of an app and a PrometheusConfig object, with each argument defaulting to the matching config field. PrometheusConfig itself is unchanged and stays the way to configure this: its middleware property now returns a configured PrometheusMiddleware instance instead of a DefineMiddleware, so middleware=[config.middleware] keeps working as before. The excluded_http_methods check stays inside handle().

Same breaking change as the other PRs in the series: exclude patterns now match the handler's path template at startup instead of the request path (documented in the changelog and whats-new-3).

Same subtlety as allowed-hosts: PrometheusConfig.scopes is user-configurable and mounted ASGI apps receive both http and websocket connections at runtime, so configured scopes are also enforced per connection via should_bypass_for_scope. Without this, restricting to scopes={"websocket"} would have silently kept instrumenting http connections through ASGI mounts.

Added some tests, covering the prometheus-specific behavior of this migration:

  • One that checks mounted ASGI apps are still instrumented. Dropping ASGI from the scopes tuple was undetected before.
  • One that checks configured scopes hold for connections through mounts (http through a mount is NOT recorded with scopes={"websocket"}). It was failing.
  • One that checks group_path=False uses the request path instead of the template. The False branch was not covered before.
  • One that checks a subclass set via middleware_class works with the new handle contract, since this PR changes that hook (no more self._config).
  • Extended the buckets assertion so default bucket boundaries must be absent - without this, dropping buckets from the property was undetected.

Two pre-existing quirks found while porting, unchanged here (possible follow-ups): request.method raises KeyError for websocket connections when excluded_http_methods is set, and PrometheusConfig.exclude_unhandled_paths is a dead field nothing reads.


📚 Documentation preview 📚: https://litestar-org.github.io/litestar-docs-preview/5006

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.21%. Comparing base (84420b9) to head (ceecd23).

Additional details and impacted files
@@                Coverage Diff                @@
##           mw/rate-limit    #5006      +/-   ##
=================================================
+ Coverage          67.19%   67.21%   +0.02%     
=================================================
  Files                293      293              
  Lines              15404    15415      +11     
  Branches            1750     1751       +1     
=================================================
+ Hits               10350    10361      +11     
  Misses              4900     4900              
  Partials             154      154              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Kumzy
Kumzy marked this pull request as ready for review August 18, 2026 10:02
@Kumzy
Kumzy requested review from a team as code owners August 18, 2026 10:02
PrometheusMiddleware moves off the legacy AbstractMiddleware base onto
ASGIMiddleware, taking its settings as keyword arguments (each defaulted
to mirror the PrometheusConfig field) instead of an app and a config.
Since the middleware is directly constructible, PrometheusConfig is
obsolete: its middleware property is deprecated (removal in 4.0) and the
documented migration path is passing a PrometheusMiddleware instance to
the middleware list directly. The excluded_http_methods check stays
inside handle(), unchanged.

PrometheusConfig.scopes is user-configurable, so like allowed-hosts the
configured scopes are also enforced per connection via
should_bypass_for_scope: mounted ASGI apps stay wrapped regardless and
their connections are filtered by scope type at runtime, keeping e.g.
scopes={'websocket'} effective for connections through mounts. exclude
patterns now match the handler's path template at startup and excluded
handlers bypass the middleware entirely, matching the other migrated
middleware.

Tests and docs examples construct the middleware directly; new tests
cover the prometheus-specific behavior of this migration: mounted apps
stay instrumented, configured scopes hold for connections through
mounts, group_path=False uses the request path, non-default buckets
replace the default boundaries, subclasses work with the new handle
contract, and the deprecated property warns while still returning a
configured instance.
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