Request-scoped logging in FastAPI

One id, every log line in that request

The goal: generate a request id when a request comes in, and have it show up automatically on every log line logged anywhere while handling that request โ€” the route, a service function three calls deep, an error handler โ€” without passing request_id as an argument through every single function signature.

contextvars (stdlib, contextlib's cousin for context-local state) is what makes this possible. A ContextVar holds a value that's local to the current async task โ€” set it once near the top of a request, and any code running as part of that same request can read it back, with no threading of the value through function parameters. structlog.contextvars builds on this: bind_contextvars(request_id=...) stores the id, and as long as your logger config includes structlog.contextvars.merge_contextvars as a processor, every subsequent logger.info(...) call in that context automatically includes it.