📜 jsonl-tail
A Resilient, Zero-Dependency tail -f for JSON Lines and LLM Eval Logs
Stream, pretty-print, and filter live JSONL without crashing on corrupt lines.
⚡ Quick Demo • 💡 Why jsonl-tail • 🎯 Key Capabilities • 🏗️ Architecture • 🚀 Quick Start • ⚖️ Comparisons
⚡ Zero-Crash Stream Processing — Unlike
tail -f file.jsonl | jq .which terminates the entire pipeline on the first non-JSON log line and buffers stdout when piped,jsonl-tailprovides atomic follow polling, fault-tolerant record pass-through, key projection, and bounded memory usage with zero external dependencies.
⚡ Quick Demo
Tailing an active LLM evaluation log with formatted JSON, metadata extraction, and safe handling of corrupt disk writes:
$ jsonl-tail examples/eval.log.jsonl
{
"timestamp": "2026-09-03T10:00:03Z",
"level": "INFO",
"run_id": "eval-001",
"model": "claude-3-5-sonnet",
"step": 4,
"score": 0.97,
"meta": {
"dataset": "human-eval",
"temperature": 0.0
}
}
[invalid json] [corrupt-disk-write-header] 0xDEADBEEF <<< corrupt log line pass-through >>>
{
"timestamp": "2026-09-03T10:00:04Z",
"level": "ERROR",
"run_id": "eval-001",
"model": "claude-3-5-sonnet",
"step": 5,
"score": 0.0,
"meta": {
"dataset": "human-eval",
"error": "rate_limit_exceeded"
}
}
{
"timestamp": "2026-09-03T10:00:05Z",
"level": "INFO",
"run_id": "eval-001",
"model": "gemini-2.5-pro",
"step": 6,
"score": 0.91,
"meta": {
"dataset": "math500",
"i18n": "日本語テスト (evaluation successful)"
}
}
Notice how the middle corrupt line is badged with [invalid json] and passes through cleanly without crashing the viewer.
💡 Why jsonl-tail?
In modern AI engineering, agent trajectories, eval benchmarks (SWE-bench, GSM8K), and model training traces all stream as append-only .jsonl files. Developers typically inspect these logs using traditional Unix pipelines:
$ tail -f run.jsonl | jq .
While convenient, this pipeline breaks down in production for three critical reasons:
- The Fatal Syntax Crash:
jqexpects 100% syntactically valid JSON. If an external library prints a raw traceback, a progress bar, or an unformatted warning directly to stdout,jqthrowsparse errorand immediately aborts the pipeline. - Standard Output Buffering: When
jqis piped into downstream commands (or monitored in subshells), its output is block-buffered by default unless explicitly invoked with--unbuffered, delaying live step updates. - Partial Write Race Conditions: Active logging processes periodically flush byte chunks before the trailing newline
\nhits disk. Standard tools attempt to parse the half-written line, corrupting terminal output.
jsonl-tail solves these edge cases out of the box. It is a single, zero-dependency CLI utility that gives you native tail -f semantics specifically engineered for JSON Lines.
🎯 Key Capabilities
| Capability | Flag & Syntax | Behavior & Guarantees | Failure Resilience |
|---|---|---|---|
| Fault-Tolerant Tail | jsonl-tail log.jsonl |
Pretty-prints the last $N$ records with 2-space indentation and ANSI coloring. | Non-JSON lines pass through raw with [invalid json] badge. |
| Live Atomic Follow | jsonl-tail -f log.jsonl |
Emits the last records and polls for newly appended records (tail -f). |
Incomplete lines lacking \n are held until fully flushed. |
| Piped Stream Tail | cat log.jsonl | jsonl-tail -n 5 |
Bounded memory consumption; correctly preserves -n count on standard input. |
Memory bounded to $O(N)$ via ring buffer. |
| Compact Mode | jsonl-tail -c log.jsonl |
Compresses output to one JSON record per line (pipe-friendly). | Strips unnecessary whitespace without modifying values. |
| Key Projection | jsonl-tail -k meta -c log.jsonl |
Extracts top-level keys; formats nested objects or primitives cleanly. | Emits [no key '<key>'] if missing; never throws KeyErrors. |
| Regex & Substring Filter | jsonl-tail --regex '"(WARN|ERROR)"' |
Retains only matching records before formatting. | Compiles pattern once; clean syntax errors on invalid regex. |
🏗️ System Architecture
jsonl-tail follows a streaming, memory-bounded pipeline designed for continuous operation:
flowchart LR
subgraph INGESTION["1. Ingestion & Buffering"]
A["File Path(s) / Piped Stdin"] --> B["Bounded Ring Buffer (deque)"]
A --> C["Atomic Follow Poller"]
end
subgraph RECOVERY["2. Fault-Tolerant Engine"]
B --> D["Line Normalizer"]
C --> D
D --> E["JSON Decoder & Guard"]
E -->|Valid Record| F["Filter & Key Projection"]
E -->|Corrupt / Raw Text| G["Pass-Through Badging"]
end
subgraph EMISSION["3. Output Pipeline"]
F --> H["Format Engine (Pretty / Compact)"]
G --> I["ANSI Color Renderer"]
H --> I
I --> J["Immediate Unbuffered Flush (stdout)"]
end
Architectural Highlights
- Bounded Memory Ingestion: Files and stdin streams are consumed through a bounded FIFO ring buffer (
collections.deque(maxlen=N)). Memory footprint remains constant regardless of whether the log is 10 KB or 50 GB. - Atomic Line Recovery: The follow engine inspects trailing byte offsets for line feeds (
\n,\r). Incomplete writes are buffered in memory and the file pointer is preserved until complete records land on disk. - Stream Auto-Reconfiguration: Standard output and error streams are dynamically reconfigured to UTF-8 with character replacement on startup, guaranteeing consistent rendering on Windows terminals and non-UTF8 locales.
🚀 Quick Start
Installation
Choose the installation method that fits your workflow:
# Method 1: Install as a standalone CLI tool via uv (Recommended)
uv tool install jsonl-tail
# Method 2: Install via standard pip
pip install jsonl-tail
# Method 3: Run instantly without installing
uvx jsonl-tail examples/eval.log.jsonl
# Method 4: Editable local development setup
git clone https://github.com/FreakyAdy/jsonl-tail.git
cd jsonl-tail
uv sync
Basic Commands
# 1. View last 10 records, formatted with syntax colors
jsonl-tail examples/eval.log.jsonl
# 2. View all records in compact one-line JSON
jsonl-tail -a -c examples/eval.log.jsonl
# 3. Follow live appended logs as they land
jsonl-tail -f examples/eval.log.jsonl
# 4. Filter for specific severity levels using regex
jsonl-tail --regex '"(WARN|ERROR)"' examples/eval.log.jsonl
# 5. Extract a specific nested key in compact format
jsonl-tail -k meta -c examples/eval.log.jsonl
# 6. Pipe input from stdin while respecting tail counts
cat examples/eval.log.jsonl | jsonl-tail -n 3 -c
💡 Terminal Tip: Set
NO_COLOR=1in your environment to disable ANSI color codes when piping to files or plain text pagers.
Runnable Examples
Explore the bundled examples/ directory to test jsonl-tail against real evaluation records:
# Inspect the sample evaluation log
jsonl-tail examples/eval.log.jsonl
# Filter for failed steps
jsonl-tail --filter "rate_limit_exceeded" examples/eval.log.jsonl
⚖️ Ecosystem Comparison
How jsonl-tail compares to existing log viewers and command-line JSON tools:
| Feature / Metric | jsonl-tail |
tail -f | jq . |
lnav |
fx / jless |
|---|---|---|---|---|
| Primary Scope | Stream & Follow JSONL | General JSON processor | Interactive log TUI | Interactive JSON tree pager |
| Fault Tolerance | ✅ 100% (Pass-through) | ❌ Aborts on corrupt line | ✅ Tolerant | ❌ Aborts on parse error |
Stream-Native (tail -f) |
✅ Built-in | ⚠️ Fragile (buffering issues) | ✅ Built-in | ❌ Static inspection only |
| Partial Write Safety | ✅ Atomic line wait | ❌ Fails parse | ✅ Tolerant | ❌ Not applicable |
| Pipe / Script Friendly | ✅ Pure CLI stream | ✅ Pure CLI stream | ❌ Full-screen TUI | ❌ Full-screen TUI |
| External Dependencies | ✅ Zero (Pure Python) | Requires jq + coreutils |
Requires C++ / libpcre | Requires Node.js / Rust |
| Cross-Platform | ✅ Linux, macOS, Windows | ⚠️ POSIX preferred | ⚠️ POSIX only | ✅ Cross-platform |
jsonl-tailis designed to be a lightweight, zero-dependency stream filter. When you need complex tree queries across deep schemas, usejq. When you need full-screen interactive log analysis, uselnav. When you want a crash-prooftail -ffor JSON Lines, usejsonl-tail.
🤝 Contributing & Community
jsonl-tail is an open-source community project. Contributions, bug reports, and ideas are warmly welcome!
- CONTRIBUTING.md: Development setup, local testing, and PR guidelines.
- CHANGELOG.md: Release history and version notes.
- Issue Tracker: Report bugs or request enhancements.
- Pull Request Template: Standard PR review checklist.
📄 License
Distributed under the MIT License.
Copyright (c) 2026 Aditya Suryavanshi
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jsonl_tail-0.1.0.tar.gz.
File metadata
- Download URL: jsonl_tail-0.1.0.tar.gz
- Upload date:
- Size: 25.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
532e85a756167b2005a68e41eda2959ae0b96f4101ec49d3d345c38985943129
|
|
| MD5 |
d2b086cbb58aec98fadfb089ecc942b3
|
|
| BLAKE2b-256 |
9a3d78f02042fffa5660ec6d588c1da7f9ec53c0565a0a51a7800cf512faab74
|
File details
Details for the file jsonl_tail-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jsonl_tail-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13a67ab3e0181b8a5dbd9bdc8b280483d621f13c0724d3cda39a88ca54ace2ef
|
|
| MD5 |
3847b80463dd1aab9c5f8f12d909ec3c
|
|
| BLAKE2b-256 |
a24a535e94145fb315b1d4f1c2c51eb79e048b3e8f68661417d993b19d993f0b
|