"""Per-request correlation id (ADR-0008, ADR-0011). Every request gets a `request_id`: reused from an incoming `X-Request-Id` if the caller supplied one, otherwise generated. It is bound into structlog's contextvars so every log line emitted while handling the request carries it, stored on `request.state` for exception handlers, and echoed back in the response header. """ import uuid from collections.abc import Awaitable, Callable from typing import override import structlog from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import Response _HEADER = "X-Request-Id" class RequestIdMiddleware(BaseHTTPMiddleware): @override async def dispatch( self, request: Request, call_next: Callable[[Request], Awaitable[Response]] ) -> Response: request_id = request.headers.get(_HEADER) or str(uuid.uuid4()) request.state.request_id = request_id structlog.contextvars.clear_contextvars() structlog.contextvars.bind_contextvars(request_id=request_id) try: response = await call_next(request) finally: structlog.contextvars.clear_contextvars() response.headers[_HEADER] = request_id return response