import time import uuid from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import Response from core.logging import get_logger, request_id_var logger = get_logger("middleware") def get_request_id() -> str: return request_id_var.get("-") class RequestLoggingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next) -> Response: req_id = str(uuid.uuid4())[:8] request_id_var.set(req_id) start_time = time.time() client_ip = request.client.host if request.client else "unknown" logger.info( f"Request started: {request.method} {request.url.path}", extra={"extra_data": {"client_ip": client_ip, "method": request.method, "path": str(request.url.path)}}, ) try: response = await call_next(request) except Exception: duration_ms = (time.time() - start_time) * 1000 logger.error( f"Request failed: {request.method} {request.url.path} ({duration_ms:.1f}ms)", exc_info=True, ) raise duration_ms = (time.time() - start_time) * 1000 logger.info( f"Request completed: {request.method} {request.url.path} -> {response.status_code} ({duration_ms:.1f}ms)", extra={"extra_data": {"status_code": response.status_code, "duration_ms": round(duration_ms, 1)}}, ) response.headers["X-Request-ID"] = req_id return response