""" Middleware for request logging and tracking. Implements correlation ID tracking for requests and comprehensive logging following 2026 FastAPI best practices. """ import logging import time import uuid from typing import Callable from fastapi import Request, Response from starlette.middleware.base import BaseHTTPMiddleware from starlette.types import ASGIApp logger = logging.getLogger(__name__) class RequestLoggingMiddleware(BaseHTTPMiddleware): """ Middleware to log all HTTP requests with timing and correlation IDs. Features: - Generates unique correlation ID for each request - Logs request method, path, and client IP - Measures and logs request processing time - Logs response status code - Adds correlation ID to response headers for tracing """ async def dispatch( self, request: Request, call_next: Callable ) -> Response: # Generate correlation ID for request tracking correlation_id = str(uuid.uuid4()) request.state.correlation_id = correlation_id # Get client IP client_host = request.client.host if request.client else "unknown" # Log incoming request logger.info( f"Request started - " f"method={request.method} " f"path={request.url.path} " f"client={client_host} " f"correlation_id={correlation_id}" ) # Process request and measure time start_time = time.time() try: response = await call_next(request) process_time = time.time() - start_time # Add correlation ID to response headers response.headers["X-Correlation-ID"] = correlation_id response.headers["X-Process-Time"] = str(process_time) # Log response logger.info( f"Request completed - " f"method={request.method} " f"path={request.url.path} " f"status={response.status_code} " f"duration={process_time:.3f}s " f"correlation_id={correlation_id}" ) return response except Exception as exc: process_time = time.time() - start_time # Log error logger.error( f"Request failed - " f"method={request.method} " f"path={request.url.path} " f"duration={process_time:.3f}s " f"correlation_id={correlation_id} " f"error={str(exc)}", exc_info=True ) # Re-raise exception to be handled by FastAPI raise class ErrorLoggingMiddleware(BaseHTTPMiddleware): """ Middleware to catch and log unhandled exceptions. Ensures all exceptions are logged before being returned to the client, providing full stack traces for debugging. """ async def dispatch( self, request: Request, call_next: Callable ) -> Response: try: response = await call_next(request) return response except Exception as exc: correlation_id = getattr(request.state, "correlation_id", "unknown") logger.error( f"Unhandled exception - " f"method={request.method} " f"path={request.url.path} " f"correlation_id={correlation_id}", exc_info=True ) # Re-raise to let FastAPI handle the response raise