Context Binding
Add request_id, user_id, or any context once—it automatically appears in every log from that request. No need to pass context through your entire call stack.
How it works
When you call logger.bind(request_id="123", user_id="abc"), that context automatically appears in every subsequent log from that request:
Bind once at the request boundary - typically in middleware or at the start of a handler
Every log includes it - no need to pass request_id as a parameter through 10 function calls
Each request gets its own context - one user’s logs won’t leak into another’s
Clear when done -
logger.clear_context()removes bound values (optional, automatic withruntime())
Sync example
from fapilog import runtime
with runtime() as logger:
logger.bind(request_id="req-123", user_id="u-1")
logger.info("Request started")
logger.clear_context()
Async example
import asyncio
from fapilog import runtime_async
async def worker(name, logger):
await logger.info(f"{name} started")
async def main():
async with runtime_async() as logger:
logger.bind(request_id="req-123")
await asyncio.gather(worker("t1", logger), worker("t2", logger))
logger.clear_context()
asyncio.run(main())
Tips
Bind at the start of each request - In FastAPI, this happens automatically with
FastAPIBuilder. In other frameworks, bind in your middleware.Use
runtime()orruntime_async()- These context managers automatically clear context when the request ends, preventing leakage.Common fields to bind:
request_id,user_id,tenant_id,trace_id,correlation_id