Logging
Decision Tree
code
What to log? → What level?
├─ Application started, config loaded → INFO
├─ Request received/completed → INFO (or DEBUG in high-traffic)
├─ User action (login, purchase, permission change) → INFO
├─ Expected failure (validation, 404) → WARN
├─ Unexpected failure (unhandled error, 500) → ERROR
├─ Variable values during debugging → DEBUG
└─ Granular tracing (entering/exiting functions) → TRACE (off in production)
Log Levels
| Level | When | Production? |
|---|---|---|
| ERROR | Unexpected failures, needs attention | Always on |
| WARN | Expected failures, degraded state | Always on |
| INFO | Business events, lifecycle events | Usually on |
| DEBUG | Diagnostic detail, variable values | Off (enable per-request) |
| TRACE | Function-level tracing | Off |
Structured Logging Format
Always log as structured JSON, not free-text strings.
code
BAD: logger.info(`User ${userId} logged in from ${ip}`)
GOOD: logger.info('User logged in', { userId, ip, method: 'password' })
json
{
"level": "info",
"message": "User logged in",
"timestamp": "2026-02-05T12:00:00Z",
"userId": "abc-123",
"ip": "192.168.1.1",
"method": "password",
"requestId": "req-456",
"service": "auth-api"
}
What to Log
| Always Log | NEVER Log |
|---|---|
| Request ID / trace ID | Passwords or password hashes |
| User ID (not PII) | API keys, tokens, secrets |
| Action performed | Credit card numbers |
| HTTP method, path, status, duration | Session tokens |
| Error messages and codes | Full request bodies with PII |
| Timestamp (ISO 8601, UTC) | Social security / national IDs |
Request Tracing
Assign a unique requestId at the edge (API gateway or first middleware). Pass it through every log entry and downstream call.
typescript
// Middleware
app.use((req, res, next) => {
req.id = req.headers['x-request-id'] || crypto.randomUUID();
res.setHeader('x-request-id', req.id);
next();
});
// Then in every log call
logger.info('Processing payment', { requestId: req.id, orderId });
Return it in error responses so users can reference it in support requests.
Tools by Stack
| Stack | Library | Key Feature |
|---|---|---|
| Node.js | pino | Fast structured JSON, low overhead |
| Node.js | winston | Flexible transports, widely used |
| Python | structlog | Structured logging, processors |
| Python | logging (stdlib) | Built-in, use with JSON formatter |
| Go | slog (stdlib 1.21+) | Structured, built-in |
Anti-Patterns
| Anti-Pattern | Fix |
|---|---|
console.log / print in production | Use structured logger |
| Logging sensitive data | Scrub PII, mask secrets |
| No request ID | Add trace ID middleware |
| String concatenation in log messages | Use structured fields |
| Logging in hot loops | Guard with level check or move outside loop |
| No log rotation / retention policy | Configure max size, retention days |