logtidy
v0.1.0
Published
Make JSON / structured logs readable in your terminal. Pretty-prints JSON log lines, passes plain lines through untouched (unlike jq). Zero dependencies.
Maintainers
Readme
logtidy
Make JSON / structured logs readable in your terminal. Pipe a noisy stream of
JSON log lines in, get a clean, colorized, one-line-per-event view out — while
every non-JSON line (your console.log, stack traces, framework banners) passes
through untouched.
Zero dependencies. Zero config. No daemon, no account, nothing to set up.
npm run dev 2>&1 | npx logtidy08:15:30.123 INFO server started port=3000 env=dev
08:15:31.044 WARN slow query db.ms=1200 table=users
08:15:31.910 ERROR request failed method=POST path=/api/pay status=500
TypeError: cannot read property 'id' of undefined ← plain lines pass straight through
at handler (/app/routes/pay.js:42:18)Why not just jq?
jq is great until your stream isn't pure JSON. Feed it one plain line — a
startup banner, a stack trace, a stray print() — and it aborts the whole
pipe. Real dev logs are always a mix. logtidy humanizes the JSON lines and
lets everything else flow by, so you never lose context.
It also speaks the common logger dialects out of the box: it finds your
timestamp (time / ts / timestamp / @timestamp), your level (level /
lvl / severity / levelname, as a word or a pino/bunyan number), and your
message (msg / message / text / event) — then lays the rest out as tidy
key=value pairs with nested objects flattened (req.headers.host).
Install
npx logtidy # no install, run on demand
npm i -g logtidy # or install the `logtidy` command globallyThere's an identical Python build too: pipx run logtidy / pip install logtidy
(see logtidy-py). Both ports are tested
against the same vectors, so they format byte-for-byte the same.
Usage
<command> | logtidy [options]
logtidy [options] < app.log| Option | Description |
| --- | --- |
| -l, --level <lvl> | Drop lines below this level (trace/debug/info/warn/error/fatal). Lines with no recognizable level are always kept. |
| -f, --fields <list> | Show only these comma-separated dot-path fields, e.g. -f level,msg,req.url. |
| --filter level=<lvl> | Alias for --level. |
| --color / --no-color | Force color on/off. Default: auto (on when stdout is a TTY; respects NO_COLOR). |
| -h, --help | Show help. |
| -v, --version | Print version. |
Examples
# only show warnings and worse
docker logs -f app | logtidy --level warn
# focus on the fields you care about
cat requests.log | logtidy --fields level,msg,req.method,req.url,res.status
# pino / bunyan apps (numeric levels) just work
node server.js | logtidyDesign notes
- One pure function at the core.
formatLine(rawLine, opts)has no I/O, no clock, no globals — it's a string→string (ornullwhen filtered). The CLI is a thin stdin→stdout wrapper around it. That's what makes the Node and Python ports verifiably identical. - Timestamps never go through a
Date. ISO strings have their time portion sliced out as-is; epoch numbers are reduced to UTC time-of-day with integer math. Deterministic, timezone-free, identical across languages. - It won't break your pipe. Non-JSON in → same line out.
EPIPE(e.g.| head) exits cleanly.
License
MIT
