FS reader
The filesystem drain writes wide events as NDJSON to .jsonl files under .evlog/logs/ (one file per day, e.g. 2026-05-08.jsonl, plus rotation suffixes like .1.jsonl when size-based rotation is enabled). The evlog/fs module also ships readers that let any Node tool replay or follow that history without hooking into the running app.
Build a script that consumes evlog's local NDJSON history (no app hook required).
- Confirm the filesystem drain is wired up (
evlog/fsadapter writing NDJSON to.evlog/logs/*.jsonl) - For replay: import
readFsLogsfromevlog/fsand iteratefor await (const event of readFsLogs({ since, until, level, filter })) - For follow mode: import
tailFsLogsand iterate the same way. It watches for new lines, handles rotation, and accepts anAbortSignal - Apply filters at read time (
level,since,until, customfilterpredicate) instead of post-processing - Treat malformed lines as silently skipped (partial writes happen), and never crash the script on a bad line
Replay history
import { readFsLogs } from 'evlog/fs'
for await (const event of readFsLogs({ since: '2026-03-01', level: 'error' })) {
console.log(event.timestamp, event.action ?? event.message)
}
readFsLogs(options) walks the NDJSON files in chronological order, parses them line by line, and yields events that pass all filters. Files outside the date window are skipped entirely.
Options
| Option | Type | Description |
|---|---|---|
dir | string | Directory to read from. Default: .evlog/logs. |
since | Date | string | Yield events with timestamp >= since. |
until | Date | string | Yield events with timestamp <= until. |
level | LogLevel | LogLevel[] | Filter by event level. |
filter | (event) => boolean | Custom predicate. |
Malformed lines (partial writes, manual edits) are silently skipped, so your script never crashes on a bad line.
Live tail
import { tailFsLogs } from 'evlog/fs'
const ac = new AbortController()
process.on('SIGINT', () => ac.abort())
for await (const event of tailFsLogs({ signal: ac.signal })) {
console.log('live:', event.action ?? event.message)
}
tailFsLogs(options) first yields existing events (unless fromEnd: true), then keeps yielding new ones as they're appended, including events written into newly created daily files. Partial writes split across polls are recombined transparently.
Tail-specific options
| Option | Type | Description |
|---|---|---|
pollIntervalMs | number | Polling interval. Default: 500ms (minimum 50ms). |
fromEnd | boolean | Skip existing events; only yield future ones. Default: false. |
signal | AbortSignal | Stop tailing when aborted. |
All readFsLogs options also apply.
Use cases
- A local Electron / Tauri dashboard reading
.evlog/logs/from a target project directory - A CI report aggregator that scans logs after a test run
- A
grep-style CLI that pipes filtered events intojq - Replaying historic events into a dashboard before switching to a live in-process subscription. See the replay-then-live recipe
Diagnostics channel
Publish every wide event on a node:diagnostics_channel, so a consumer subscribes without importing evlog and Cloudflare can Tail Worker it.
Recipes
Concrete copy-paste recipes — build your own minimal devtool, pipe to curl + jq, replay history then go live, and aggregate on the consumer side.