Skip to content
Aabha AI Academy

Module 8 of 9 · Lesson 22 of 26

Trace requests with redacted structured logs

Work through trace requests with redacted structured logs using a runnable reference, a focused regression check and a local extension.

Read in any order. All lessons stay open, including after an unanswered or incorrect check.

In this lesson you will trace requests with redacted structured logs. Work with the Equipment Rental API in the downloadable lab. The reference is a complete solution with separate lesson checks, so you can inspect the answer, make a deliberate local change and verify its behavior.
Useful request logs connect one failure to its surrounding events without copying secrets. RequestSignals accepts only UUID-shaped correlation ids; other input is replaced with a fresh UUID. It returns the id in a response header and logs an allow-list of request id, method, route pattern, status and elapsed milliseconds.

It does not log Authorization, raw query strings, bodies or the raw path containing record identifiers. A route pattern such as /rentals/{identifier} also keeps metric labels bounded as users and records grow. An unmatched path uses one shared label instead of creating a new series for every typo or attack string. JSON encoding prevents newline characters from becoming separate fabricated log entries. Correlation ids identify traces, not accounts, and cannot authorize a request. Choose additional fields deliberately, with redaction and retention review, rather than dumping a request object. The reference records a numeric status and duration so you can follow a failure before investigating deeper application evidence.
Worked source: equipment/observability.py, RequestSignals.

Run lesson22. A request includes a secret-like query value, a bearer header and an invalid correlation id. The response must contain a valid UUID, while the captured structured log contains neither secret-like value. Inspect the allow-list in the middleware rather than relying on a blacklist of known password names.
pythonCopyable
class RequestSignals:
    def __init__(self, app, counters):
        self.app = app
        self.counters = counters

    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            return await self.app(scope, receive, send)
        raw = dict(scope.get("headers", [])).get(b"x-request-id", b"")
        try:
            request_id = str(uuid.UUID(raw.decode("ascii")))
        except (ValueError, UnicodeError):
            request_id = str(uuid.uuid4())
        scope.setdefault("state", {})["request_id"] = request_id
        status = 500
        started = time.monotonic()

        async def traced_send(message):
            nonlocal status
            if message["type"] == "http.response.start":
                status = message["status"]
                message["headers"].append((b"x-request-id", request_id.encode()))
            await send(message)

        try:
            await self.app(scope, receive, traced_send)
        finally:
            route = getattr(scope.get("route"), "path", "unmatched")
            method = (
                scope["method"]
                if scope["method"]
                in {"GET", "POST", "PATCH", "DELETE", "PUT", "OPTIONS", "HEAD"}
                else "OTHER"
            )
            self.counters[(method, route, f"{status // 100}xx")] += 1
            logger.info(
                json.dumps(
                    {
                        "request_id": request_id,
                        "method": method,
                        "route": route,
                        "status": status,
                        "duration_ms": round((time.monotonic() - started) * 1000, 2),
                    }
                )
            )
TerminalPython 3.13 virtual environment; Docker running; extracted lab directory
python run_checks.py -k lesson22

Expected result The selected lesson test passes against a new temporary PostgreSQL database; the container is removed afterward.

Keep for reference

Equipment Rental lab and lesson checks

ZIP containing Python source, real Alembic migrations, 26 lesson checks, a dependency lock and text instructions. Extract it before following the local exercise.

Download Equipment Rental lab and lesson checks

Practise locally

Send a valid UUID correlation id and assert it is echoed and logged. Add a request with a newline-containing invalid id and confirm the output remains one JSON record. Test that a rental UUID appears only in the response contract, not as a unique metrics label or raw logged route.

The lesson check verifies the reference behavior. Add your own assertions for your change. Local practice is not uploaded or scored by this learning release.

Pause and reflect

What failure does this lesson prevent, and which assertion in lesson22 would expose it?

Use a concrete input, expected result and limitation from your local work. Saving a reflection does not certify the project.

Optional knowledge check

Which field is appropriate for request metrics and logs here?

The matched route pattern, without raw query values or bearer tokens.

Correct. Patterns keep labels bounded and avoid copying sensitive request details.

The full URL and Authorization header for every request.

Try another answer. Raw URLs and credentials can disclose private values and create unbounded labels.

Log only raw request paths; they never contain identifying values.

Try another answer. Paths can contain record ids; prefer matched route patterns.

This practice does not assess your project or award a certificate.

Your reading progress

Progress is saved in this browser when storage is available.

Sign in to save across devices · Create an optional account