Spaces:
Sleeping
Sleeping
| """Request logging middleware — attaches request_id to every log line.""" | |
| from __future__ import annotations | |
| import uuid | |
| from typing import Any, Callable | |
| from starlette.middleware.base import BaseHTTPMiddleware | |
| from starlette.requests import Request | |
| from starlette.responses import Response | |
| from src.utils.logger import Timer, get_logger, request_id_var | |
| logger = get_logger(__name__) | |
| class RequestLoggingMiddleware(BaseHTTPMiddleware): | |
| async def dispatch(self, request: Request, call_next: Callable) -> Response: | |
| req_id = str(uuid.uuid4()) | |
| token = request_id_var.set(req_id) | |
| with Timer() as t: | |
| logger.info( | |
| "request_start", | |
| extra={ | |
| "method": request.method, | |
| "path": request.url.path, | |
| "query": str(request.url.query), | |
| }, | |
| ) | |
| try: | |
| response = await call_next(request) | |
| except Exception: | |
| logger.exception( | |
| "request_unhandled_exception", | |
| extra={"method": request.method, "path": request.url.path}, | |
| ) | |
| raise | |
| finally: | |
| request_id_var.reset(token) | |
| status = getattr(response, "status_code", 0) | |
| level = logger.warning if status >= 400 else logger.info | |
| level( | |
| "request_end", | |
| extra={ | |
| "method": request.method, | |
| "path": request.url.path, | |
| "status": status, | |
| "duration_ms": t.ms, | |
| }, | |
| ) | |
| response.headers["X-Request-ID"] = req_id | |
| return response | |