Skip to main content

Add request context to Python logs without exposing payloads

2026-11-11 15:57:30.309456+00:00
Add request context to Python logs without exposing payloads

When several jobs run together, timestamps alone do not explain which request caused a log entry. Attach a small correlation value to each job and log selected operational fields. Copying whole request bodies into logs makes both investigation and data handling harder.

Interleaved log lines grouped by job ID, with a redacted token.

Log an allowlisted event

This example uses a LoggerAdapter to attach a synthetic job ID. It deliberately selects safe fields rather than logging the incoming mapping.

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s job=%(job_id)s %(message)s",
)
base = logging.getLogger("export")

def log_started(job_id, request):
    logger = logging.LoggerAdapter(base, {"job_id": job_id})
    safe = {
        "record_count": request["record_count"],
        "authorization": "[REDACTED]",
    }
    logger.info("started count=%s auth=%s",
                safe["record_count"], safe["authorization"])

log_started("fixture-42", {
    "record_count": 7,
    "authorization": "dummy-secret",
    "customer_email": "person@example.invalid",
})

The email is excluded, and the authorization value is replaced before message formatting. The original request object is never passed to the logger. That is a narrower boundary than a regex applied later to arbitrary formatted text.

The formatter above expects a job_id field. If other records share the same handler without that field, formatting can fail. In a larger application, use a dedicated handler or a filter that supplies a default value. Test third-party log records before changing the root logging format.

Check all paths that reach the log sink

Capture logs in a test and assert that "dummy-secret" and the synthetic email are absent. Then exercise error handling. An exception may contain the full URL, query string or provider response body even when the ordinary event message is clean.

Correlation IDs should be bounded and safe to display. If an ID comes from an inbound header, validate its length and characters or replace it with a generated value. Preserve a separate mapping only if the application needs it.

A job ID does not need to identify a person. Avoid using email addresses or access tokens as correlation values. Carry the ID through an enqueue message and worker context when a request starts background work, so both sides can be investigated together.

Retention, access controls and export destinations still matter after the message format is improved. Redacting one field is a tested local behavior, not proof that an entire system never logs secrets.

Postzyo is related background for queued work. Python documents logging adapters and formatting.