log-management
Log Management
A log line's only job is to be found and understood by someone who wasn't there when it was written. A free-text sentence optimized for a human reading it in a terminal fails that job the moment volume grows past what one person can scroll through — it can't be queried, filtered, or aggregated reliably. Structured logging exists to make every line a queryable record instead of a string.
At scale, logs stop being something a person tails in a terminal and become a dataset something else queries on that person's behalf — every choice below follows from designing for that querying system, not for the human glancing at a live stream.
If a log line can't be filtered by field without a regex, it isn't structured yet.
1. Emit structured fields, not formatted sentences
Every log line should be a JSON object (or equivalent structured format) with consistent field names across the codebase — level, timestamp, service, message, plus whatever request-specific fields apply. "user 4821 failed login from 10.0.0.4" cannot be filtered by user ID without a regex; {"event":"login_failed","user_id":4821,"source_ip":"10.0.0.4"} can be filtered, aggregated, and joined with other events in one query.
- Use a shared field schema across services —
user_idin one service anduserIdin another silently breaks every cross-service query. - Name the event, don't just describe it in prose —
event: "login_failed"is filterable; a sentence about a failed login is not. - Keep the human-readable
messagefield too, but treat it as a summary, not the source of truth the query relies on.
Done when: every log line is machine-parseable and field names are consistent across every service that emits them.