Metadata-Version: 2.4
Name: logtidy
Version: 0.1.0
Summary: Make JSON / structured logs readable in your terminal. Pretty-prints JSON log lines, passes plain lines through untouched (unlike jq). Zero dependencies.
Author: yyfjj
License: MIT
Project-URL: Homepage, https://github.com/jjdoor/logtidy-py
Project-URL: Repository, https://github.com/jjdoor/logtidy-py
Project-URL: Issues, https://github.com/jjdoor/logtidy-py/issues
Keywords: log,logs,logging,json,pretty,ndjson,jsonlines,pino,bunyan,cli,structured-logging,devtools
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Logging
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# 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 `print()`, stack traces, framework banners) passes
through *untouched*.

Zero dependencies (pure standard library). Zero config. No daemon, no account.

```bash
python app.py 2>&1 | pipx run logtidy
```

```
08: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
  Traceback (most recent call last):                     ← plain lines pass straight through
    File "app.py", line 42, in handler
```

## Why not just `jq`?

`jq` is great until your stream isn't pure JSON. Feed it one plain line — a
startup banner, a traceback, 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

```bash
pipx run logtidy           # no install, run on demand
pip install logtidy        # or install the `logtidy` command
```

There's an identical Node build too: `npx logtidy` / `npm i -g logtidy`
(see [logtidy](https://github.com/jjdoor/logtidy)). Both ports are tested against
the same vectors, so they format byte-for-byte the same.

## Usage

```bash
<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

```bash
# 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

# structlog / python-json-logger output just works
python -m myapp | logtidy
```

## Design notes

- **One pure function at the core.** `format_line(raw_line, opts)` has no I/O, no
  clock, no globals — it's a string→string (or `None` when 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 `datetime`.** 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. `BrokenPipeError`
  (e.g. `| head`) exits cleanly.

## License

MIT
