askmydocs / app /observability /request_context.py
Prabu17's picture
Upload folder using huggingface_hub
f8b48da verified
Raw History Blame Contribute Delete
2.59 kB
"""Thread-local request context for the current request_id.
This module stores the current request's identifier in a
:class:`contextvars.ContextVar` so any code running inside the request --
including deeply nested service code, background tasks, and observability
helpers -- can attach the request_id to log records and traces without the
caller having to thread it through every function signature.
The :class:`RequestContext` context manager sets the value on entry and
restores the previous value on exit, so nested contexts (e.g. a span
triggering an internal sub-request) work as expected.
"""
from __future__ import annotations
import contextvars
import uuid
# A ContextVar is the right tool: it's safe across threads and async tasks
# and is automatically scoped per asyncio task.
_request_id_var: contextvars.ContextVar[str | None] = contextvars.ContextVar(
"request_id", default=None
)
class RequestContext:
"""Context manager that sets the current request_id.
Example::
with RequestContext() as request_id:
logger.info("processing") # log record is auto-tagged
# Or with an explicit id:
with RequestContext("req-abc123"):
...
"""
def __init__(self, request_id: str | None = None) -> None:
self._explicit = request_id
self._generated: str | None = None
self._token: contextvars.Token[str | None] | None = None
def __enter__(self) -> str:
rid = self._explicit or str(uuid.uuid4())
if self._explicit is None:
self._generated = rid
self._token = _request_id_var.set(rid)
return rid
def __exit__(self, *exc_info: object) -> None:
if self._token is not None:
_request_id_var.reset(self._token)
self._token = None
def get_request_id() -> str | None:
"""Return the current request_id, or None if no context is active."""
return _request_id_var.get()
def set_request_id(request_id: str) -> RequestContext:
"""Return a context manager that sets the current request_id for its block.
Convenience wrapper for use with ``with``::
with set_request_id("req-123"):
do_work()
"""
return RequestContext(request_id)
def clear_request_id() -> None:
"""Reset the current request_id to None.
Mainly useful in tests.
"""
_request_id_var.set(None)
__all__ = [
"RequestContext",
"clear_request_id",
"get_request_id",
"set_request_id",
]