evlog logs
evlog logs reads the events the file system drain wrote and shows them the way you would want to read them: one line per request, the thing that mattered at the end of it, and one request in full when you have its id. It reads files; it never touches the app.
evlog logs
6 events · .evlog/logs
08:00:00 GET /api/health 200 2ms
10:00:00 POST /api/checkout 402 412ms ✗ Error: Payment processing failed · Card declined by issuer
10:30:00 GET /api/reports 200 1.2s report.id=r-1
11:00:00 POST /api/refund 200 80ms audit billing.refund user:usr_42 → invoice:inv_1 success
11:30:00 GET /api/items 500 30ms
11:45:00 GET /api/items warn 700ms user.id=usr_7
evlog logs errors — the 2 that failed · evlog logs <requestId> — one request in full · evlog logs slow — worst first
The newest event is at the bottom, like tail. Nothing is written: the fs drain writes on every request and this reads what it wrote, both the compact and the pretty: true layout, across the dated files.
Four views
| Command | Shows |
|---|---|
evlog logs | The last 50 events, oldest first |
evlog logs errors | Events that failed: a 5xx status, an error or fatal level, or an error block |
evlog logs slow | Events over 500ms, worst first (--over 1s to move the bar) |
evlog logs <requestId> | Every event carrying that id, in full: request, error with why and fix, audit record, then the business fields. The first block of a UUID is enough |
Filters
Filters compose with any view.
| Flag | What it does |
|---|---|
--since <when> | Only events after: a duration back from now (15m, 2h, 3d) or a date (2026-10-01, 2026-10-01T09:00) |
--until <when> | Only events before, same spellings |
--level <a,b> | Only these levels: trace, debug, info, warn, error, fatal |
--path <path> | Only requests on this exact path |
--status <n> | Only this status (404) or class (4xx) |
--over <duration> | For slow: what a request has to exceed (default 500ms) |
--limit <n> | Most events to show (default 50) |
--dir <path> | Read another directory (default: the project's .evlog/logs, or the directory its fs drain is configured for) |
-f, --follow | Keep reading as the app writes, like tail -f; Ctrl-C stops |
--json | The events as JSON on stdout |
--cwd <dir> | Another project in the workspace |
A time, level, status or limit that cannot be read stops the command (exit 2) rather than silently widening the query.
evlog logs errors --since 1h
evlog logs slow --over 2s --path /api/reports
evlog logs --status 5xx --level error,fatal --limit 20
evlog logs -f --path /api/checkout
For agents
--json returns one envelope: the directory, the view, how many events matched, and the events shown.
evlog logs errors --since 30m --json
{
"schemaVersion": 2,
"dir": "/app/.evlog/logs",
"view": "errors",
"count": 2,
"matched": 2,
"events": [ { "timestamp": "…", "path": "/api/checkout", "status": 402, "error": { "data": { "why": "…", "fix": "…" } } } ]
}
With --follow --json, each new event is one JSON line on stdout, since a stream has no end. The analyze-logs skill calls evlog logs first and falls back to reading the files only when the CLI is not available.
What it will not do
- It reads local files. A remote drain (Axiom, Datadog, …) has its own query language and UI; this command does not wrap them.
- One directory per run. In a monorepo, run it from the app (
--cwd apps/web) or pass--dir. - No aggregation yet. Counts by route or status are a
jqaway from--json; astatsview may come later.
Next
- File system drain: what writes the files, rotation,
pretty evlog map: which handlers emit a wide event at all