A logger emits a record through configured handlers, and a formatter chooses which fields appear in its output.
Python logging: include bounded context without printing sensitive payloads
Operation contract
The receipt audit logger has one local handler and disables propagation to avoid duplicate delivery through a parent handler in this fixture. The format includes level and logger name, while parameter substitution records only a bounded identifier and outcome. It never prints a full receipt payload, authorization header or email address.
Failure and ownership boundary
Configuring a logger repeatedly can attach repeated handlers and multiply output. Formatting arguments later also does not make evaluation of an expensive argument expression lazy. Use an enabled-level guard for expensive diagnostic work and define retention, access and redaction separately from this handler setup. Python exceptions: translate an input error without hiding its cause provides an error contract for callers rather than relying on logs as a response.
Working program
import io
import logging
captured = io.StringIO()
logger = logging.getLogger("aitrove.receipts.fixture")
logger.handlers.clear()
logger.propagate = False
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(captured)
handler.setFormatter(logging.Formatter("%(levelname)s %(name)s %(message)s"))
logger.addHandler(handler)
try:
logger.info("receipt=%s outcome=%s", 41, "accepted")
print(captured.getvalue().strip())
finally:
logger.removeHandler(handler)
handler.close()Output
INFO aitrove.receipts.fixture receipt=41 outcome=acceptedCosts and limits
This fixture retains one short log line in memory. Handler I/O, queue growth and retention cost are not bounded by the logger name or level setting.
Common Mistakes
- Do not attach a new handler for every request.
- Diagnostic context should not contain credentials or entire payloads.
Connected lessons
Python exceptions: translate an input error without hiding its cause, Python thread pools: collect results and observe worker failures, Spring Boot metrics: bound tag values instead of tracking each receipt.
Related Python operation checks
Python logging: allowlist fields before they reach a handler.
Follow the related contract
Python ContextVar: task-local labels without sharing one global binding.
