""" Logging middleware for FastAPI. Wraps each request in a ``log_operation("http_request")`` so the started/success/failed pattern from the OCI Logging policy is enforced uniformly, and clears all contextvars on the way out to avoid leaking correlation IDs between requests. """ import uuid from typing import Callable from fastapi import Request, Response from starlette.middleware.base import BaseHTTPMiddleware from src.core.logging import ( clear_context, get_logger, log_operation, set_request_id, ) logger = get_logger(__name__) class LoggingMiddleware(BaseHTTPMiddleware): """ Generates a request_id, populates the logging contextvar, and emits start/success/failed logs via ``log_operation``. Context is cleared on exit so subsequent requests start from a clean slate. """ async def dispatch(self, request: Request, call_next: Callable) -> Response: request_id = str(uuid.uuid4()) request.state.request_id = request_id set_request_id(request_id) method = request.method path = request.url.path client_host = request.client.host if request.client else None try: async with log_operation( "http_request", component="api", logger=logger, method=method, path=path, client_host=client_host, user_agent=request.headers.get("user-agent"), ) as op: response = await call_next(request) op.add_field("status_code", response.status_code) response.headers["X-Request-ID"] = request_id return response finally: clear_context()