The Fallacy of the First-Class Log: Making Everything Critical Makes Nothing Critical
For years, the best practice has been clear: log everything. Structure your logs. Emit rich, machine-parseable JSON with consistent fields for every transaction, every user request, every internal state change. We’ve built elaborate pipelines to ingest these rivers of data, convinced that if we just capture enough, the truth will reveal itself in a dashboard. We’ve turned our application logs into first-class citizens of our observability stack. I think we’ve made a profound mistake.
The counterintuitive truth is this: by elevating all logs to ‘first-class’ status, we have paradoxically devalued their signal. When every log entry is a beautifully formatted event with a `severity`, a `transaction_id`, a `user_context`, and a `microtimestamp`, we stop reading them. We delegate their meaning entirely to the aggregation system. The log ceases to be a human-readable narrative for the operator and becomes mere fodder for a metrics generator or an AI anomaly detector. The crucial, subtle distinction between ‘noise we can pattern-match’ and ‘a clue worth pondering’ is lost at the source.
The Tyranny of Structure
This structural tyranny has a silencing effect. A developer, knowing that any `console.log` or `print` statement is now a deviant act against the logging schema, will often choose to say nothing rather than craft a temporary, exploratory message. The friction is too high. But those off-script, one-off logs—‘Huh, the cache key for user X looks odd here’ or ‘Entering the legacy codepath for region Y’—are often the very breadcrumbs that lead you out of the forest during a novel failure. By forbidding ‘second-class’ logs, we sterilize our systems of their most human debugging traces.
Furthermore, this approach creates a monolithic log stream where the truly critical events—the ‘database is down’ or ‘authentication provider unreachable’—must scream to be heard over the deafening roar of ten thousand ‘200 OK’ request logs. We then solve this by adding more complexity: elaborate filtering, sampling rules, and dynamic log levels. We’ve built a Rube Goldberg machine to recreate what a simple, thoughtful separation of channels once gave us for free: a place for verbose machine data, and a separate, quieter place for urgent human news.
I propose a modest heresy. Keep your structured log firehose for the machines. Let it churn out its JSON for billing, aggregates, and broad-stroke performance graphs. But alongside it, nurture a plain text log file. A file with a different name, perhaps `ops.log` or `notable.txt`. This is not a journal for the machines; it is a shared notebook for the caretakers of the system. Write to it rarely. Write to it in full sentences. Let its entries be events a human would genuinely want to read: ‘Manually disabled region failover for maintenance window.’ ‘Spike in errors from legacy API client; contacted admin.’ ‘First successful run of the new migration path.’
This log is not first-class. It has no schema. It might not even be ingested into your central system. Its value lies precisely in its second-class nature—it is a backchannel for meaning, a space for context that doesn’t fit into a prescribed field. In making this distinction, you restore the hierarchy of attention. The structured logs feed the machines. The plain text log speaks to the people. And in that quiet, purposeful separation, you might just find you can hear your system think again.
Notes & further reading
A few pages I came back to while writing this:
- Madison, WI
- The Lineman's Vow: A Pre-Internet Promise of Uptime
- Milwaukee, WI
- The Unseen Tenant in the Server Closet
- a useful directory
- The Lost Art of the Fixed Address
- a local resource
- a place-by-place guide
- one area's overview
- a regional guide
- a helpful reference
- a practical rundown
- a nearby resource