Skip to content

Instantly share code, notes, and snippets.

@szapp
Created October 3, 2025 19:06
Show Gist options
  • Select an option

  • Save szapp/2caa8c23a64e909c3987226ed6a79cab to your computer and use it in GitHub Desktop.

Select an option

Save szapp/2caa8c23a64e909c3987226ed6a79cab to your computer and use it in GitHub Desktop.
Simple context in Python's stdlib logging
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = []
# ///
import logging
import logging.config
from contextlib import contextmanager
from contextvars import ContextVar
from typing import Iterator
context = ContextVar("context", default={})
@contextmanager
def log_context(**kwargs) -> Iterator[dict]:
"""Temporarily bind and restore context in a with-block."""
token = context.set({**context.get(), **kwargs})
try:
yield context.get()
finally:
context.reset(token)
class ContextFormatter(logging.Formatter):
"""A formatter that injects contextvars into the record."""
def format(self, record: logging.LogRecord) -> str:
record.context = ", ".join(f"{k}={v}" for k, v in context.get().items())
return super().format(record)
"""
Config
"""
logging_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"context": {
"()": "__main__.ContextFormatter",
"format": "[ %(levelname)s ] %(name)s %(message)s %(context)s",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "context",
"level": "INFO",
"stream": "ext://sys.stdout",
}
},
"root": {
"level": "INFO",
"handlers": ["console"],
},
}
logging.config.dictConfig(logging_config)
"""
Demo
Output:
[ INFO ] __main__ Hello from myfunc! request_id=abc123, user=alice
[ INFO ] __main__ Temporary override request_id=abc123, user=2433
[ INFO ] __main__ Looping request_id=abc123, user=alice, loop_iter=0
[ INFO ] __main__ Looping request_id=abc123, user=alice, loop_iter=1
[ INFO ] __main__ Looping request_id=abc123, user=alice, loop_iter=2
[ INFO ] __main__ After loop request_id=abc123, user=alice, probe={'a': [1, 2]}
[ INFO ] __main__ Clean again
"""
logger = logging.getLogger(__name__)
def myfunc():
logger.info("Hello from myfunc!")
with log_context(request_id="abc123", user="alice"):
myfunc()
with log_context(user=2433):
logger.info("Temporary override")
with log_context() as ctx:
for i in range(3):
ctx["loop_iter"] = i
logger.info("Looping")
with log_context(probe=dict(a=[1, 2])):
logger.info("After loop")
logger.info("Clean again")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment