starlette_exporter
starlette_exporter copied to clipboard
Starlette_exporter ignores responses from other middleware
I use starlette_exporter alonside asgi-ratelimit, and it seems starlette_exporter ignores the HTTP 429 responses from that ratelimiting middleware.
Not sure about the root cause, but I created a dummy middleware that successfully catches and prints those 429 responses. That's why I decided it is more likely to be a starlette_exporter peculiarity.
To reproduce:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from ratelimit import RateLimitMiddleware, Rule
from ratelimit.backends.simple import MemoryBackend
from ratelimit.types import Scope
from starlette.middleware.base import BaseHTTPMiddleware
from starlette_exporter import PrometheusMiddleware, handle_metrics
app = FastAPI()
async def _dummy_auth_function(scope: Scope) -> tuple[str, str]:
return "dummy_uid", "default"
app.add_middleware(
RateLimitMiddleware,
authenticate=_dummy_auth_function,
backend=MemoryBackend(),
config={
r"^/test/ratelimited": [Rule(minute=1)],
},
)
app.add_middleware(
PrometheusMiddleware,
filter_unhandled_paths=True,
)
app.add_route("/metrics", handle_metrics)
class StatusCodeLoggerMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
print(f"Returned status code: {response.status_code}")
return response
app.add_middleware(StatusCodeLoggerMiddleware)
@app.get("/test/ratelimited")
async def test_ratelimited():
return {"message": "Hello World"}
@app.get("/test/failing")
async def test_failing():
return JSONResponse(status_code=429, content={"message": "Too many requests"})
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, port=8001, log_config=None)
Now if we query it:
curl -Sv http://localhost:8001/test/ratelimited 2>&1 |grep '< HTTP' # HTTP 200
curl -Sv http://localhost:8001/test/ratelimited 2>&1 |grep '< HTTP' # HTTP 429
We would not see anything 429-related in the metrics:
curl -sS http://localhost:8001/metrics |grep 429 # nothing
However we can see 429 status codes, if they were returned from the endpoint function itself:
curl -Sv http://localhost:8001/test/failing 2>&1 |grep '< HTTP' # HTTP 429
curl -sS http://localhost:8001/metrics |grep 429 # 20 rows of starlette_request... metrics