Motivation
The previous issues (#6/#7) were closed in favor of a single, consolidated task.
We need production-grade logging with per-request correlation and automated
verification in one PR.
Goals
- Generate a UUIDv4 per HTTP request and propagate it via:
• X-Request-ID response header
• a contextvars.ContextVar so any log line can include it
- Emit a single access-log entry per request (method, path, status, latency)
- Enrich every log record with the request ID using a
logging.Filter
- Provide full unit-test coverage for the above behaviour
- Use only the Python stdlib + FastAPI (no new deps)
Implementation Plan
-
New module app/logging_config.py
a. _request_id_ctx: ContextVar[str|None] = ContextVar("request_id", default=None)
b. class RequestIdFilter(logging.Filter) sets record.request_id
c. configure_logging() calls logging.basicConfig(...) with format:
%(asctime)s [%(levelname)s] [%(request_id)s] %(name)s: %(message)s
and attaches the filter to the root logger.
d. add_request_id_middleware(app: FastAPI) -> None — middleware that:
• creates uuid.uuid4().hex per request
• stores in ctx-var, records start time
• awaits call_next
• logs access line via logging.getLogger("app.access")
• resets ctx-var and sets X-Request-ID header
-
Modify app/main.py
• Import and call configure_logging() before FastAPI(...)
• Call add_request_id_middleware(app) right after app creation
• Remove old logging.basicConfig() block
-
Unit tests tests/test_request_id_logging.py
def test_request_id_header(client):
r = client.get("/health")
rid = r.headers.get("X-Request-ID")
assert rid and len(rid) == 32 and int(rid, 16)
def test_request_id_in_logs(client, caplog):
caplog.set_level("INFO")
r = client.get("/health")
rid = r.headers["X-Request-ID"]
assert any(getattr(rec, "request_id", None) == rid for rec in caplog.records)
• Re-use existing client fixture from tests/test_main.py.
-
Docs (tiny)
• Update README.md within the same PR: add a "Logging & Request IDs"
subsection explaining header + log format.
Acceptance Criteria
uvicorn app.main:app runs; logs show request IDs
- Every response has an
X-Request-ID header
pytest passes; new tests succeed; coverage ≥ current baseline
- No new runtime dependencies
Files touched / added
• app/logging_config.py (NEW)
• app/main.py (EDIT)
• tests/test_request_id_logging.py (NEW)
• README.md (EDIT)
Labels
enhancement, logging
Motivation
The previous issues (#6/#7) were closed in favor of a single, consolidated task.
We need production-grade logging with per-request correlation and automated
verification in one PR.
Goals
•
X-Request-IDresponse header• a
contextvars.ContextVarso any log line can include itlogging.FilterImplementation Plan
New module
app/logging_config.pya.
_request_id_ctx: ContextVar[str|None] = ContextVar("request_id", default=None)b.
class RequestIdFilter(logging.Filter)setsrecord.request_idc.
configure_logging()callslogging.basicConfig(...)with format:%(asctime)s [%(levelname)s] [%(request_id)s] %(name)s: %(message)sand attaches the filter to the root logger.
d.
add_request_id_middleware(app: FastAPI) -> None— middleware that:• creates
uuid.uuid4().hexper request• stores in ctx-var, records start time
• awaits
call_next• logs access line via
logging.getLogger("app.access")• resets ctx-var and sets
X-Request-IDheaderModify
app/main.py• Import and call
configure_logging()beforeFastAPI(...)• Call
add_request_id_middleware(app)right after app creation• Remove old
logging.basicConfig()blockUnit tests
tests/test_request_id_logging.py• Re-use existing
clientfixture fromtests/test_main.py.Docs (tiny)
• Update
README.mdwithin the same PR: add a "Logging & Request IDs"subsection explaining header + log format.
Acceptance Criteria
uvicorn app.main:appruns; logs show request IDsX-Request-IDheaderpytestpasses; new tests succeed; coverage ≥ current baselineFiles touched / added
•
app/logging_config.py(NEW)•
app/main.py(EDIT)•
tests/test_request_id_logging.py(NEW)•
README.md(EDIT)Labels
enhancement,logging