logfmt vs JSON logs: what a parser actually needs from a line
Both formats carry the same four things. They differ in what happens to the fifth — the line that does not fit.
The four things
A log line becomes a record when a parser can find a timestamp, a level, a message, and the fields — the key=value pairs that follow the message in logfmt, or the object keys in JSON lines. With those, a run of lines becomes rows you can filter by level, search by field, and compare against another run by record rather than by line order.
Where logfmt falls short
logfmt is readable in a terminal and cheap to write, and a parser has to guess its shape: which token is the level, where the message ends and the pairs begin, whether a value with spaces was quoted. A stack trace breaks it outright — the continuation lines match no pattern. The honest answer is a pattern shown before it is applied, a match count, and the unmatched lines kept rather than dropped.
unstringify scores a pasted log against 9 built-in shapes — Kibana Discover · copied rows, Kibana document · field per line, Apache / nginx combined, syslog · RFC 5424, syslog · RFC 3164, Rails / Ruby logger, bracket prefix + JSON tail, timestamped logfmt, logfmt — and shows the winner as chips with a live preview: “matches N of M lines · K kept as raw”. A regex of your own replaces the guess, and a pattern you wrote is saved for the next file that matches it.
Where JSON lines fall short
JSON lines need no pattern: every line is a document, every key is a field. What they lose is readability, and a single broken line — a log rotated mid-write, a payload with a raw newline — is a broken document if you treat the file as one JSON array. Treat each line on its own and the damage stays on that line. The five-line sample on the NDJSON page has 1 broken line; 4 read, one is counted and shown, and “broken only” filters to it.
Which to write
If a machine reads your logs first — a collector, a search index — write JSON lines and keep the payload fields flat. If a person reads them first — a terminal, a tail — write logfmt with the timestamp and level first, and quote values that hold spaces. Either way, keep one record per line: the parser, whichever it is, is counting lines.