logging-patterns · git:20260618.9d9291d · 2026-06-18 · sha256 926c4452dc39ae82
logging-patterns git:20260618.9d9291dA
Immutable. This exact content never changes and is served at /api/v1/blob/926c4452dc39ae82.
---
name: logging-patterns
description: Logging conventions — level usage, formatting style, structured output.
when_to_use: Writing code that logs events, configuring log output, or choosing log levels.
---
# Logging Conventions
## Setup
One logger per module, at module level:
```python
import logging
logger = logging.getLogger(__name__)
```
## Formatting
Use `%s`-style formatting arguments, not f-strings — the message template is preserved for structured aggregator queries:
```python
logger.info("Cleaned up %d expired sessions", count) # yes
logger.error("SMTP send failed for %s", email, exc_info=True) # yes
logger.info(f"Cleaned up {count} expired sessions") # no
```
## Level Conventions
| Level | Use for |
|---|---|
| `DEBUG` | Cache hit/miss, slow-path internals (opt-in only) |
| `INFO` | Startup/shutdown, admin bootstrap, cleanup counts, rate limit hits |
| `WARNING` | Recoverable anomalies, swallowed exceptions, degraded operation |
| `ERROR` | Unexpected exceptions on operational paths — always with `exc_info=True` |
| `CRITICAL` | Reserved for unusable state |
## Output
One record per line on stdout. Let the container runtime (Docker, k8s) capture it — no file sinks or log rotation in-app.
Support two formats via config:
- **`plain`** — Readable for local dev
- **`json`** — Stable single-line object per record for log aggregators (Loki, Datadog, ELK, CloudWatch)
Timestamps in UTC in both formats.