| name | logs-server |
| description | Query a local Sentry-shaped log sink (per-session SQLite under `.davstack/logs/<name>.db`) for cross-service timelines, error windows, or `trace_id` / `run_id` slices. Use when the user references a failed request, agent run, or test invocation and you need the actual log timeline rather than guessing. Also use when planting hypothesis-driven probes during a `diagnose` loop. Skip for tail-following live stdout (use the dev server directly) or for prod logs (this is local-only). |
First run davstack check to confirm the daemon is running.
The read path is sqlite3
logs-server has no query CLI verb (removed in 2.1.0). You read the store directly with sqlite3 against .davstack/logs/<db> — works even if the ingest daemon isn't running. You only need serve running for the app to write new envelopes.
Standard incantation
sqlite3 -header -column .davstack/logs/<db> "<SQL>"
-header prints column names. -column aligns the output as a table. Without these you get pipe-separated values with no labels — fine for piping, terrible for reading.
Picking the DB
.davstack/logs/
default.db ← un-tagged emissions
reorder-bug.db ← runs started with --db=reorder-bug
hotfix-7c.db
default.db is the catch-all. Named siblings appear when a transmitter stamps a davstack-logs.db attribute. The file IS the session boundary.
Schema
logs(id, ts, recv_ts, project, service, run_id, trace_id, span_id,
level, severity_number, logger, msg, data, attrs, tag)
Indexed on (project, run_id, trace_id, level, ts) — filter on those first.
msg — log body, ANSI prefix stripped. Indexed-string predicates are cheap here.
data — raw Sentry log record JSON, verbatim. Includes OTel-typed attributes.
attrs — flat {key: value} JSON of data.attributes, OTel {value, type} wrapper stripped. Populated at insert time. Reach in with attrs->>'<key>' — much shorter than the four-segment data.attributes.<key>.value path.
tag — promoted from diag.tag (nullable).
Need the OTel type discriminator? data->>'$.attributes.<key>.type' — rarely needed.
Investigation pattern
-
Get a trace_id or run_id from the user (the chat message, the error stamp, or the URL hash that holds __diagRunId).
-
Timeline for one run / trace:
sqlite3 -header -column .davstack/logs/default.db "
SELECT ts, level, msg
FROM logs
WHERE run_id = '<id>'
ORDER BY ts;
"
-
Narrow to errors + surrounding context:
sqlite3 -header -column .davstack/logs/default.db "
SELECT ts, level, msg
FROM logs
WHERE level = 'error'
ORDER BY ts DESC LIMIT 20;
"
-
Slice by structured probe attribute (the killer recipe):
sqlite3 -header -column .davstack/logs/default.db "
SELECT ts, msg,
attrs->>'seam' AS seam,
attrs->>'row_count' AS row_count
FROM logs
WHERE run_id = '<id>'
AND attrs->>'tags' LIKE '%H3%'
ORDER BY ts;
"
-
For fat data payloads, project specific fields with data->>'$.<path>' rather than dumping the whole data blob.
If a query returns empty or hits the wrong DB, the failure is usually in the sink config or transmitter — davstack check reports the resolved path and recent row count; see setup.md for transmitter wiring.
For hypothesis-driven probe-then-slice debugging, see writing-logs.md.
Reference
CLI reference
logs-server — Local Sentry-shaped log ingest. Read the store with sqlite3 against .davstack/logs/ (see docs/reading-logs.md).
logs-server serve — Boot the log-ingest HTTP endpoint
logs-server refresh — Evict the daemon's cached DB handles and re-read config without restarting (keeps the daemon PID alive). Pass --hard for a full shutdown + detached re-serve (loses PID; needed for port/host/cors changes).
logs-server health — Daemon liveness check
logs-server doctor — Validate local install (node, config, db rows, daemon liveness)
Run logs-server <command> --help for the full flags and options of any command.