A self-contained observability stack for OpenCode. OpenCode does not emit
telemetry on its own — opencode-plugin-otel
is the plugin that instruments it and exports traces, metrics, and logs over OTLP. This repo is
the receiving end for that data: an OTLP collector, Tempo, Prometheus, and Loki feeding a Grafana
dashboard with cost/token analytics, TraceQL-based explainability, and a standalone session
timeline (waterfall) explorer. Without the plugin installed in OpenCode, there is nothing for any
of this to show.
This repo, especially the grafana dashboard and docker compose config, is mostly taken from ColeMurray/claude-code-otel. The trace explorer serves as a local debugging tool only (asof now) and could be in a next step generalized to work for more coding agents by adapting to different OTEL exporters.
| Grafana dashboard | Trace Explorer — Overview |
|---|---|
![]() |
![]() |
| Trace Explorer — Sessions |
|---|
![]() |
- Docker with Compose v2 (
docker compose) just(optional — wraps common commands)- Bun (only for Option B — building the plugin from source)
uv(only forjust setup—scripts/setup.shusesuv run pythonto editopencode.json)
docker compose up -d
# or: just upThe plugin exists in two places:
-
DEVtheOPS/opencode-plugin-otel— upstream, published to npm as@devtheops/opencode-plugin-otel. Options A and B below install from here. -
schemaitat/opencode-plugin-otel— André Schemaitat's fork. Synced with upstream and adds an opt-in long-running session span mode: withOPENCODE_LONG_RUNNING_SESSION_SPANS=1(or thelongRunningSessionSpansplugin option) a single rootopencode.sessionspan stays open for a primary session's entire lifetime — fromsession.createdthroughsession.deleted— so every turn's LLM and tool spans nest under one cohesive trace instead of a separate root span per turn. This change is currently in PR #98 (not yet merged upstream). Trace Explorer works either way — without the flag its session timeline still renders, the spans just aren't necessarily nested under a single root session span.Upstream behaviour (default): the root session span is closed on each
session.idle, and every subsequent user turn gets its own independent root run span keyed by message ID — producing separate trace trees per turn rather than one session-wide tree. The fork's flag is off by default, so it matches this behaviour unless you enable it. Subagent sessions nest under their parent session's trace regardless of the flag.
Option A — npm (recommended, no build step):
Add the plugin to ~/.config/opencode/opencode.json (merge into your existing config):
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@devtheops/opencode-plugin-otel"]
}OpenCode installs it automatically at startup from the npm registry. The published package includes pre-built output — no Bun required.
Option B — local file: path (for development/customization):
Clone and build the plugin manually first — OpenCode does not build it for you:
git clone https://github.com/DEVtheOPS/opencode-plugin-otel ~/projects/opencode-plugin-otel
cd ~/projects/opencode-plugin-otel
bun install
bun run buildThen reference it by absolute path:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["file:///home/you/projects/opencode-plugin-otel"]
}Note:
~is not valid here — use the full absolute path. OpenCode symlinks the directory into its cache; ifdist/is missing or stale the plugin loads silently but does nothing. Re-runbun run buildafter any source change.
Git URLs (e.g.
github:DEVtheOPS/opencode-plugin-otel) do not work — thepreparescript is absent so the build never runs anddist/is never produced.
Option C — automated (just setup):
just setupRuns scripts/setup.sh: adds the upstream npm plugin
(@devtheops/opencode-plugin-otel) to ~/.config/opencode/opencode.json and starts the stack — all
in one step. This is the scripted equivalent of Option A (no clone, no build); OpenCode installs the
plugin from npm at startup. For the long-running single-session trace behaviour, install the fork
manually via Option B and set OPENCODE_LONG_RUNNING_SESSION_SPANS=1 (see step 3) until
PR #98 lands upstream.
Export environment variables before running opencode:
export OPENCODE_ENABLE_TELEMETRY=1
export OPENCODE_OTLP_ENDPOINT=http://localhost:4317
export OPENCODE_OTLP_PROTOCOL=grpc
# Optional (fork / Option C only): keep one root span open for the whole primary
# session so all turns nest into a single trace, matching the Trace Explorer timeline.
export OPENCODE_LONG_RUNNING_SESSION_SPANS=1Or add these to your shell profile (.bashrc, .zshrc, etc.).
Verify the plugin is configured and built before running OpenCode:
just check-otel-pluginDashboards will be empty until you run OpenCode (step 5).
| URL | Service |
|---|---|
| http://localhost:3000 | Grafana — "OpenCode Observability" (admin/admin) |
| http://localhost:8060 | Trace Explorer |
| http://localhost:9090 | Prometheus |
| http://localhost:3200 | Tempo |
| http://localhost:4317 | OTLP collector — gRPC ingest (used by the plugin) |
| http://localhost:3100 | Loki |
Now when you run opencode, traces and metrics will be emitted to the local OTLP collector and visible in the dashboards above.
See the Quick Start guide for the full walkthrough.
Stop the stack:
docker compose down
# or: just downRemove the plugin config:
Remove the plugin entry from ~/.config/opencode/opencode.json:
{
"plugin": ["@devtheops/opencode-plugin-otel"]
}Delete that line (or the entire "plugin" key if it's the only entry).
If you installed from source (Option B), also delete the clone:
rm -rf ~/projects/opencode-plugin-otelAlso remove the telemetry env vars from your shell profile if you added them.
- Cost & token analytics — usage by model, agent, and session; breakdown by token type
- Tool usage — call counts, average duration, and success rate per tool
- Productivity — lines added/removed, message counts per session
- TraceQL tables — LLM calls (prompt → model → outcome) and tool calls (tool → params → result)
- Trace → Logs linking — click a span in Grafana to jump to matching Loki logs
- Trace Explorer — React/FastAPI waterfall SPA with session timelines, subagent linking, and a cross-session usage dashboard
- Dashboard filters —
session_id,model, andagenttemplate variables apply to every panel
The @devtheops/opencode-plugin-otel plugin
instruments OpenCode and exports the following (default OTel prefix opencode.). Counters surface in
Prometheus with . → _ and a _total suffix — e.g. opencode.session.count becomes
opencode_session_count_total. See docs/telemetry.md for the full label sets.
opencode.session.count— sessions started (is_subagent)opencode.lines_of_code.count— lines of code modified (type=added/removed)opencode.commit.count— Git commits createdopencode.cost.usage— session cost by model and agent (USD)opencode.token.usage— token usage by type (input/output/reasoning/cacheRead/cacheCreation)opencode.cache.count— cache read/creation eventsopencode.message.count— assistant messages by model and agentopencode.model.usage— requests per model and provideropencode.tool.duration— tool execution duration histogram (tool_name,success)opencode.session.duration— session duration histogramopencode.subtask.count— subagent (task tool) invocationsopencode.retry.count— API request retries
user_prompt— user prompt submissions (prompt_length,model)api_request— API requests with duration, token counts, and costapi_error— API errors with status codestool_result— tool execution results and timings (tool_name,success,duration_ms)tool_decision— tool permission decisionssubtask_invoked— subagent task invocationssession.created/session.idle/session.deleted/session.error— session lifecycle with runningtotal_*counterscommit— Git commit activity
Full documentation is published at
schemaitat.github.io/opencode-otel-observability,
or browse it directly in docs/:
- Architecture — how the services fit together
- Quick Start — start the stack and connect OpenCode
- Metrics, Logs & Traces — what's exported and how to query it
- Dashboard — the Grafana "OpenCode Observability" dashboard
- Trace Explorer — the standalone waterfall + usage dashboard UI
- Configuration — collector, Tempo, and Grafana datasource details
- Development —
justtargets and local dev workflow
This repo, especially the grafana dashboard and docker compose config, is mostly taken from ColeMurray/claude-code-otel.


