diff --git a/src/main.py b/src/main.py index 49622d2..60bfc3d 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,10 @@ """FastAPI app factory. Composes all domain routers and middleware.""" import logging +import time +from starlette.middleware.base import BaseHTTPMiddleware +from starlette.requests import Request +from starlette.responses import Response from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware @@ -23,6 +27,34 @@ logging.basicConfig( datefmt="%Y-%m-%d %H:%M:%S", ) +logger = logging.getLogger(__name__) + + +# ── Request Logging Middleware ───────────────────────────────────── + +class RequestLoggingMiddleware(BaseHTTPMiddleware): + """Log request method, path, status code, and duration.""" + + async def dispatch(self, request: Request, call_next) -> Response: + start = time.time() + method = request.method + path = request.url.path + + response = await call_next(request) + + duration = (time.time() - start) * 1000 # ms + status = response.status_code + + # Log at appropriate level + if status >= 500: + logger.error("%s %s -> %d (%.1fms)", method, path, status, duration) + elif status >= 400: + logger.warning("%s %s -> %d (%.1fms)", method, path, status, duration) + else: + logger.info("%s %s -> %d (%.1fms)", method, path, status, duration) + + return response + def create_app() -> FastAPI: """Create and configure the FastAPI application. @@ -48,6 +80,9 @@ def create_app() -> FastAPI: allow_headers=["*"], ) + # Request logging + app.add_middleware(RequestLoggingMiddleware) + # Register exception handlers app.add_exception_handler(ChunkingError, chunking_exception_handler) app.add_exception_handler(BenchmarkError, benchmark_exception_handler)