Community Python Snippet
A Tracer Decorator With Arg Redaction (Python)
A `@trace()` decorator I bolt onto Python services when the production logs go quiet at the wrong layer. Logs entry, exit, duration, and exceptions, with secret-arg redaction baked in.
A Tracer Decorator With Arg Redaction (Python)
A `@trace()` decorator I bolt onto Python services when the production logs go quiet at the wrong layer. Logs entry, exit, duration, and exceptions, with secret-arg redaction baked in.
By @elisehuang
March 17, 2026
·
Updated May 20, 2026
862 views
6
4.4 (12)
I keep this in a tracer.py next to my dotfile pretty-printer. The trick is inspect.signature(fn).bind_partial(*args, **kwargs), which gives me a name-keyed view of arguments regardless of how the caller passed them. That lets the redact set work on password whether the caller did login('me', 'hunter2') or login('me', password='hunter2'). The _safe_repr helper truncates long blobs so a single 50KB request body does not bury the rest of the log. Drop the decorator on a suspect function during an incident and remove it after the postmortem.
Once I have more than one decorated function in the call chain, line-by-line logs lose context: which enter matches which exit? contextvars.ContextVar solves it cleanly because it follows asyncio task boundaries (unlike thread-locals). Each call mints a new span_id, captures the previous one as parent, and the structured-log shipper can reconstruct the call tree later without OpenTelemetry. I have used this in production at two startups while we were still six months from being able to justify a full tracing vendor.
The synchronous version above wraps coroutines incorrectly: it logs exit immediately because fn(*args, **kwargs) returns a coroutine object, not the awaited result. The fix is to detect coroutine functions with asyncio.iscoroutinefunction(fn) and pick a sync or async wrapper accordingly. I always run the async branch through await so the duration reflects real wall-clock time. If you skip this branch your async traces will all show 0.0ms, which is the kind of bug you only catch by running the decorator under load.
