UN Uptime Notes
Reference

A field guide to log formats

Computer code displayed on a monitor screen in close-up.
Photo: Rob Wingate / Unsplash

Anyone who has tried to grep through a mixed pile of logs from five different services knows the actual problem is rarely volume — it's that no two of them agree on where the timestamp goes, what counts as a field, or whether the thing in front of you is even meant to be machine-readable at all.

This is a working reference for the log formats you'll actually run into operating real servers, what each one is good at, and where they trip people up.

Why the format matters more than the volume

A million lines of consistently structured JSON is easier to work with than ten thousand lines of five different ad-hoc text formats, because the cost of logs isn't storage — it's the time spent writing a one-off parser at 2am for a format nobody standardized. Picking a format (and sticking to it) for anything you control is one of the highest-leverage, lowest-effort decisions in running a server.

Plain text and syslog: RFC 3164 vs. RFC 5424

Syslog is the oldest format still in everyday use, and "syslog" actually refers to two different specs that don't fully agree with each other. RFC 3164, the original 2001 BSD syslog format, looks like this:

<34>Oct 11 22:14:15 mymachine su: 'su root' failed for lonvick on /dev/pts/8

RFC 5424, from 2009, tightened the format considerably and added structured data support and a version field:

<34>1 2026-10-11T22:14:15.003Z mymachine su - ID47 - 'su root' failed for lonvick on /dev/pts/8

The practical issue is that a huge amount of still-deployed software emits 3164-style messages while claiming to speak syslog generically, which is why log shippers like rsyslog and syslog-ng ship with separate parsers for both and some amount of "best effort" fallback for messages that match neither cleanly.

JSON Lines, and why most modern tools want it

JSON Lines (one valid JSON object per line, newline-delimited, no wrapping array) has become the default expectation for anything built in the last decade — Fluentd, Vector, Loki, and most hosted logging platforms parse it natively with zero configuration:

{"ts":"2026-10-11T22:14:15Z","level":"error","service":"api","msg":"connection refused","host":"db-1","retry":3}

The appeal is that every field is already named and typed, so there's no regex guessing what the third space-delimited token means. The cost is verbosity — JSON Lines logs run noticeably larger than an equivalent plain-text line — and the fact that a single malformed line (a stray unescaped character from user input bleeding into a log message, say) breaks that one line's parsing instead of degrading gracefully the way plain text does.

Common Log Format and Combined Log Format

Still the default for Apache and widely supported by nginx, Common Log Format (CLF) is what most people picture when they think "web server log":

127.0.0.1 - frank [11/Oct/2026:22:14:15 +0000] "GET /apache_pb.gif HTTP/1.0" 200 2326

Combined Log Format adds the referrer and user-agent fields that CLF leaves out, which is why it's the more commonly configured option in practice despite CLF being the nominal default:

127.0.0.1 - frank [11/Oct/2026:22:14:15 +0000] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"

Both are space-delimited with quoted sub-fields, which makes them readable by eye and annoying to parse reliably with a naive split-on-space approach — the quoted request and user-agent fields can themselves contain spaces, which trips up more hand-rolled parsers than it should.

journald and Windows Event Log: structured but binary

Tip

Don't treat journald's binary storage as a reason to avoid it. journalctl -o json exports every entry as structured JSON on demand, which makes it one of the easier formats to pipe into a modern log aggregator despite looking opaque at rest.

Both journald (the logging component of systemd, standard on most modern Linux distributions) and Windows Event Log store entries in a binary, indexed format rather than flat text, which trades human-readability-at-rest for fast structured querying: journalctl -u nginx --since today or filtering Windows Event Viewer by event ID and source are both faster than grepping an equivalent-sized text file would be. The catch is that both require their platform's own tooling to read at all — you can't just cat a journald binary log the way you can a syslog file.

A terminal window showing a command-line interface with a sudo prompt.
Photo: Gabriel Heinzer / Unsplash

Fields worth standardizing on regardless of format

Whatever format you end up with, a consistent core set of fields pays for itself: an ISO 8601 timestamp with timezone (not "2:14pm," not a bare Unix epoch with no documentation of which epoch), a severity level using the same small vocabulary everywhere (debug/info/warn/error/fatal, not a mix of "WARNING" in one service and "warn" in another), a service or component name, and a request or trace ID that lets you follow one request's path across every service it touched. None of this requires a particular format —ad even plain text benefits from picking a consistent field order and sticking to it.

Picking a format when you control the source

For anything new you're building or configuring yourself: JSON Lines to stdout, let the container runtime or systemd capture it, and ship it from there with whatever aggregator you're already running. It's the format every modern tool assumes by default, it's trivial to add a field to later without breaking existing parsers, and the verbosity cost is almost always worth it the first time you need to filter logs by a field that would have required a regex in any text format. Reserve the older formats — syslog, CLF — for software that only speaks them natively, and don't fight it; normalize at the collection layer instead of trying to force an old daemon into a format it was never built to emit.