Logging: platform log page
Nexus aggregates logs from every component — nexus-core, nexus-worker,
nexus-telegram, and the per-run nexus-agent-runner pods — into one queryable
place: the Admin UI Logs page. Use it to confirm agents connect, NATS and
MongoDB are reachable, and LLM calls aren't erroring.
Why this design (NATS and MongoDB)
The two systems play different roles:
| Concern | Choice | Why |
|---|---|---|
| Transport | NATS (plain pub/sub) | Every component already has (or can get) a NATS connection — including the confined, ephemeral agent pods, which have no MongoDB credentials and shouldn't. Decouples emitters from storage. |
| Storage / query | MongoDB capped collection | A capped collection is fixed-size, insertion-ordered, and self-trimming — ideal for logs. It's queryable (component / level / text / time) to back the UI page. |
| Node-level baseline | stdout (JSON) | kubectl logs and any cluster log stack keep working unchanged. |
Logs are published on the plain (core) NATS subject logs.<component>,
deliberately outside the nexus.> JetStream stream, so high-volume log
traffic never bloats the durable event log.
Pipeline
tracing::info!/warn!/error! (any component)
│ CaptureLayer (nexus-observability)
▼
in-process channel ──pump──▶ NATS logs.<component> (also → stdout JSON)
│
log sink (nexus-worker) subscribes logs.>
▼
MongoDB capped collection `logs`
▲
GET /v1/logs (nexus-core) ◀── Admin UI “Logs” page (polls)
- Capture —
nexus_observability::init("<component>")installs atracingsubscriber with a JSON stdout layer plus a capture layer that turns each event (severity ≥INFOby default) into aLogRecordon an in-process channel. - Ship — long-lived services spawn
pump_logs(...)to publish records tologs.<component>. The short-lived agent runner instead drains its buffer to NATS right before exit, so agent connectivity / LLM-call logs aren't lost. - Sink — the worker runs the single
logs.>subscriber and inserts each record into the cappedlogscollection. - Read —
GET /v1/logs?component=&level=&search=&since=returns newest-first (capped$naturalorder). The UI polls every few seconds while Live is on.
Configuration
| Env var | Default | Purpose |
|---|---|---|
NEXUS_LOG | info | stdout EnvFilter (e.g. nexus_worker=debug,info). |
NEXUS_LOG_CAPTURE | info | Minimum severity captured for the page (debug/info/warn/error). |
NEXUS_LOGS_CAP_BYTES | 268435456 (256 MiB) | Capped collection size. |
NEXUS_LOGS_CAP_DOCS | 1000000 | Capped collection max documents. |
All components need NEXUS_NATS_URL set to ship logs; the worker additionally
writes to MongoDB. If NATS is unreachable a component still logs to stdout — it
just won't appear on the page.
Troubleshooting
- Page is empty — check the worker's log sink subscribed (
log sink: subscribe failedon stderr means NATS is down) and that the cappedlogscollection exists. - An agent's logs are missing — agent pods must have
NEXUS_NATS_URLand egress to NATS (allowed by the agentNetworkPolicy). Run-level outcomes also appear on the Runs page regardless. - No logs anywhere —
nexus_observability::init(...)must run at startup; it installs the only subscriber.