How to use request_id while logging in asynchronous functions?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
In asynchronous code, several requests can be in flight at once, so plain log messages quickly become hard to follow. A request_id solves that by attaching the same identifier to every log line produced while one request is being processed.
In Python async code, the correct tool for this is usually contextvars. A plain global variable breaks as soon as concurrent tasks overlap, while a context variable flows correctly through await boundaries.
Why contextvars Matters
With asyncio, multiple coroutines share the same thread. That means thread-local storage is not enough for per-request logging in many async applications. contextvars.ContextVar stores data in the logical execution context, so each request can keep its own identifier even when other requests are running at the same time.
Start by defining a request-id context variable:
Now each request can set its own value without trampling another request's logs.
Add the Request ID to Log Records
A clean pattern is to use a logging filter that reads the current context variable and injects it into each log record:
Every emitted log record now has a request_id field derived from the current async context.
Set the Request ID per Request
Inside your async request handler, set the context variable at the beginning and reset it at the end:
Because the request ID is in a context variable, each concurrent request keeps the correct identifier through await points.
Framework Integration
In web frameworks, the same idea usually belongs in middleware. The middleware reads an incoming request header such as X-Request-ID, or generates one if the client did not send it, then stores it in the context variable before the rest of the request runs.
That gives you one consistent ID across:
- access logs
- application logs
- downstream service calls
- error reports
If your service calls another service, forward the same request ID so cross-service tracing stays coherent.
Avoid Global Variables and Manual String Concatenation
A weak implementation often looks like this:
- store the request ID in a module-level global
- pass
request_idmanually into every logging call - prepend it into message strings by hand
That tends to break under concurrency or become repetitive. A context variable plus a logging filter keeps the logging call sites clean while still ensuring the ID is attached automatically.
Combine with Structured Logging
If you use JSON logs or structured logging libraries, inject the request ID as a field rather than embedding it only in the message string. That makes filtering and searching much easier in log aggregation systems.
The principle stays the same: bind the request ID to the async context and have the logger read it automatically.
Common Pitfalls
- Using a global variable for the request ID in async code, which causes requests to overwrite each other.
- Forgetting to reset the context variable after the request finishes.
- Manually adding the request ID to some log messages but not all of them.
- Generating a new request ID deep inside helper functions instead of once at the request boundary.
Summary
- Use a
ContextVarto storerequest_idin Python async code. - Add the current request ID to every log record with a logging filter or structured logger hook.
- Set the request ID at the request boundary, usually in middleware or the top-level handler.
- Reset the context when the request is done.
- Avoid globals and manual string formatting for per-request logging state.
Related reading
- How to use rolling update to re-pull container image?
- How to use skaffold with volumes
- How to use sudo inside a docker container?
- How to use the kubernetes go-client to get the same Pod status info that kubectl gives
- How to use RestSharp with async/await
- How to use the background thread in Objective-C?
- How to use string.replace in python 3.x
- How to use subprocess command with pipes

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.