slug: monitoring

Logs & Monitoring

OAR exposes its traffic through three complementary surfaces: JSONL log files on disk, a real-time SSE stream into the Web UI, and structured usage/activity queries. The Capture feature layers on top to record full request/response pairs for protocol debugging.

Log files

Logs are written as JSONL (one JSON object per line) under ~/.open-agent-router/logs/ with daily-rolling filenames:

~/.open-agent-router/logs/
├── proxy-2026-07-16.log
└── proxy-2026-07-17.log
  • Daily rolling — a new file is created at local midnight.
  • Asynchronous batch append — log entries are buffered and flushed every 500ms, so logging never blocks a response.
  • JSONL — easy to consume with jq, grep or any structured-log pipeline.
Reading JSONL with jq

jq . ~/.open-agent-router/logs/proxy-2026-07-17.log pretty-prints each entry. Pipe through jq 'select(.tag=="[route]")' to filter by tag.

Log tags

Every log entry carries a tag that classifies the event. The tags you'll see most often:

TagMeaning
[route]A request was routed. Format: [route] <method> <project>/<agent> → [group] account/model @ targetUrl (convert, try N)
[debug]Verbose diagnostic output. Useful when reproducing edge cases.
[retry]A retry was triggered — either cross-model within the group, or same-model after a transient failure.
[upstream]Upstream call lifecycle events (connection, headers, response status, timing).
[no-model]The requested model alias did not resolve to any model in the group.
[no-route]The request path did not match any project/agent/group route.
[error]An unrecoverable error — upstream timeout, adapter failure, etc.

What's not logged

To keep the log volume sane, Morgan HTTP logging skips:

  • Polling paths/api/health, /api/activity, /api/activity/status, /api/accounts-health, /api/capture/status, /api/service/status.
  • SSE endpoints/api/logs/stream.
  • Static resources — files served from ui-dist/.

Routing, retries, upstream calls and errors are always logged regardless.

Web UI Logs view

The Logs page in the Web UI streams entries live over Server-Sent Events from /api/logs/stream. Open the page and traffic appears in real time — no refresh, no polling.

  • Real-time SSE streaming at /api/logs/stream.
  • Each entry rendered with its tag, timestamp and full message.
  • Capture-related entries surface a [oar-capture:filename] chip — click it to open the capture record directly.

The same Logs view is available in CLI mode at http://127.0.0.1:26969 — point a browser at the backend and you get the full Web UI.

Dashboard

The Dashboard view is the landing page of the Web UI. It shows overview metrics at a glance:

  • Health status — per-account and per-model reachability.
  • Recent activity — the last few requests with status and latency.
  • Top talkers — accounts/groups/models ranked by request count and token spend.

Pair the Dashboard with the Logs view for a full live picture: the Dashboard summarises, the Logs view drills in.

Usage and activity

Use the oar CLI for structured queries over the activity log. Both commands hit the admin API and return either human-readable tables or, with --json, machine-readable output.

CommandDescription
oar usage [--start-time <t>] [--end-time <t>]Aggregate token spend and request counts by model/group over a window.
oar activity [--limit <n>]List the most recent requests (default 50).
usage — bash
$ oar usage --start-time 2026-07-01 --end-time 2026-07-17
# account requests tokens_in tokens_out
# deepseek 312 482k 1.2M
# anthropic 87 120k 640k
oar activity --limit 5
# 2026-07-17 14:21 /project/gz/v1/chat/completions 200 1.4s

Capture

Capture records full request/response pairs for debugging — invaluable when adapting a new protocol, diagnosing a 422 from an upstream, or reproducing a routing edge case. Each capture session runs for a fixed duration and writes one JSON file per recorded request to ~/.open-agent-router/captures/.

CLI

CommandDescription
oar capture start [--duration <sec>]Start a capture session (default 300s).
oar capture stopStop the active capture early.
oar capture statusReport whether a capture is running and how long remains.
oar capture get <filename>Read a capture record as JSON.
capture — bash
$ oar capture start --duration 60
# capture started, runs for 60s
oar capture status
# running: yes started: 14:21:03 remaining: 47s
oar capture get 2026-07-17-1421.json
# { in: {...}, out: {...}, model: claude-sonnet-4, route: project/gz }

Web UI

In the Web UI, capture is driven from a drawer that you open from the Logs page:

  • Initial request on drawer open — fetches the current capture state.
  • Final request on countdown — fetches the final state when the timer hits zero.

The capture status does not use SSE or polling — just these two HTTP requests.

The capture file list updates live from [oar-capture:filename] log chips pushed via the SSE log stream. Click a chip to open the capture record inline.

Capture runs off the main thread

Capture file writing happens in a dedicated worker_threads worker. Recording high-traffic periods does not block request handling on the main thread.

Where files are stored

Capture records are plain JSON files under ~/.open-agent-router/captures/:

~/.open-agent-router/captures/
├── 2026-07-17-1421.json
├── 2026-07-17-1422.json
└── ...

Each file contains the inbound request (in), the outbound upstream call (out), the resolved model and the route that was used.

Activity monitoring

The combination of Dashboard, Logs view and oar activity covers day-to-day monitoring:

  • Live triage — open the Logs view, watch [route] and [retry] entries flow, click capture chips to inspect.
  • Trend analysis — run oar usage over a window to see which accounts and models are carrying load.
  • Recent historyoar activity --limit 50 lists the latest requests with status and latency.
  • Per-account health — the Dashboard surfaces reachability per account and per model, including models in cooldown.

For the underlying CLI commands, see the CLI Reference. For configuration knobs like the model cooldown, see Configuration.