heisentest → guides

How to read JSON logs without losing your mind

Structured logging won: pino and winston in Node, zap and logrus in Go, structlog in Python, Docker's json-file driver. Machines love the result. Humans get a wall of {"level":30,"time":1753495200123,"msg":...} that is unreadable at 2 a.m.

The terminal way: jq

cat app.log | jq -r '[.time, .level, .msg] | @tsv'
jq 'select(.level >= 50)' app.log       # errors only (pino numeric levels)
jq 'select(.service == "checkout")' app.log

jq is superb once you know the field names — and that is the catch. Every logger spells things differently: time vs timestamp vs @timestamp, level as a word ("error") or a number (50), msg vs message. You end up reverse-engineering the schema before you can read a line.

The browser way: drop the file, get a timeline

heisentest recognizes JSON lines automatically and already knows the common field spellings: pino's numeric levels, winston's string levels, the usual timestamp keys, service and logger names. Each line becomes a clean row — time, level badge, service, message — and the noise fields wait in a detail view until you ask.

Try it: drop a JSON-lines file on heisentest.com — free, no account, open source.

Honest limits

heisentest reads one JSON object per line (the JSON-lines convention). A single giant JSON array, or pretty-printed multi-line objects, are not log files in this sense — for those, jq remains the right tool.