Skip to content

Add request-scoped UUID logging middleware + unit tests #9

Description

@mattmorgis

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

  1. 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
  2. Emit a single access-log entry per request (method, path, status, latency)
  3. Enrich every log record with the request ID using a logging.Filter
  4. Provide full unit-test coverage for the above behaviour
  5. Use only the Python stdlib + FastAPI (no new deps)

Implementation Plan

  1. 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

  2. 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

  3. 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.

  4. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions