TL;DR: Logging Cheatsheet¶
Short attention span edition. Do these things and your logs will be useful, consistent, and easy to analyze.
The 80/20 Rules¶
Use event-style messages (kebab-case) as the primary message
Examples:
"user-login","cache-miss","payment-failed"
Always log structured fields (no f-strings in the message)
Put data in fields; optionally add
_replace_msgfor a human sentence
Pick the right level:
debug— internal operations, noisy detailsinfo— business events and major milestoneswarning— unusual but non-failing conditionserror— failures and handled exceptionscritical— system unusable or operator action required
Include stable context fields:
Common:
user_id,request_idorcorrelation_id,operation,component,status_code
Measure time:
Prefer
duration_msfor operations, or log*-started/*-finishedpairs
Never log secrets/PII:
Mask tokens, passwords, and personal data; consider PII redaction in your logging design
Keep the cardinality low (avoid unbounded label values)
Exceptions — do this¶
Inside
except, preferlog.exception(...)— it automatically includesexc_info=Trueand adds the full traceback to theexceptionfield. No need forerror=str(e):
try:
do_work()
except Exception:
log = structlog.get_logger()
log.exception(
"work-failed",
operation="do_work",
)
# optionally: raise
Alternative (when you need
log.errorfor specific reasons):
try:
do_work()
except Exception as e:
log.error(
"work-failed",
operation="do_work",
exc_info=True, # oder: exc_info=e
)
Why this matters: stogger processes
exc_infoand renders a full traceback into theexceptionfield. Withoutexc_info, the stack trace is lost.Never use
exc_infooninfo/debug— too noisy and unnecessary.
Message formatting¶
Use
_replace_msgto add a readable sentence while keeping structure:
log.info(
"user-login",
_replace_msg="User {username} logged in from {ip}",
username=user.username,
user_id=user.id,
ip=request.remote_addr,
)
Output Blocks¶
Use
_raw_outputto embed tool output with ANSI colors (console gets colors, file gets stripped):
log.warning("type-errors",
_raw_output_prefix="ty",
_raw_output=ty_result.output.strip())
See Logging Patterns: Output Rendering for the full table of output keys.
Naming and consistency¶
Prefer these field names (keep them consistent):
IDs:
user_id,request_id,correlation_idTiming:
duration_msHTTP:
method,path,status_codeGeneric:
operation,component
Sampling and throttling¶
Avoid chatty
infologs in tight loops; usedebug, sample, or aggregateConsider adding a
sample_ratefield to mark sampled events
Do / Don’t¶
Do:
log.info("order-created", _replace_msg="Order {order_id} created for user {user_id}", order_id=oid, user_id=uid, total_cents=total)
Don’t:
log.info(f"order {oid} by user {uid} created with total {total}")
FAQ¶
Do we still need
exc_info=True? Yes, if you want a stack trace. Uselog.exception(...)(preferred) or passexc_info=True/exc_info=eon error logs. stogger’s processors will capture and format it into theexceptionfield.Can I log plain strings? Technically yes, but you lose structure. Always prefer event name + fields.
See also: full guide in Logging Patterns.