Guide
Do you need an OpenTelemetry stack to monitor Claude Code?
No. You need something that accepts OTLP, which can be a hosted backend you already pay for, a collector writing to a store you run, or an agent on the machine. The full collector-plus-Prometheus-plus-Grafana build is one valid shape, and it is the most expensive of the three to keep running.
What a stack actually has to do
Strip the word stack out and there are four jobs. Three of them are solved by products you can pick off a shelf. The fourth is the one nobody puts in the estimate.
- Accept an OTLP push. Any OTLP-capable backend, or a collector in front of one.
- Store it for as long as you plan to ask questions of it.
- Query and draw it, which is a dashboard tool plus somebody's afternoon.
- Get the exporter configuration onto every engineer's machine, and keep it there through reinstalls, new starters and dotfile resets.
Job four is not a technology problem, which is exactly why it does not appear in the technology comparison. It is the only one that scales with headcount.
Three shapes, honestly costed
| Shape | What you run | What you own indefinitely |
|---|---|---|
| Point the exporters at an OTLP backend you already pay for | Nothing new | Config distribution, ingest cost, and the dashboards you draw |
| Collector, metrics store, dashboard tool | Three services | All of the above, plus uptime, upgrades, retention policy and the cardinality bill |
| An agent on the machine that terminates OTLP locally | An installer | Whatever the agent does not do, and its platform limits |
If you are already in row one, row one wins and it is not close. Adding a vendor to receive telemetry that your existing vendor would receive for free is a hard thing to defend in a review.
The cardinality trap in the defaults
This is the part that turns a working prototype into a surprise invoice, and Anthropic documents it plainly enough that there is no excuse for being caught by it.
- session.id is attached to every metric datapoint, and OTEL_METRICS_INCLUDE_SESSION_ID defaults to true. Session identifiers are unbounded: a new value every session, forever.
- user.account_uuid and user.account_id are both on by default, behind OTEL_METRICS_INCLUDE_ACCOUNT_UUID.
- app.version and app.entrypoint default to false, so those two are already the sensible way round.
- OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES defaults to true, which is how your own OTEL_RESOURCE_ATTRIBUTES keys (team.id, department) reach the datapoints.
In a time-series store, an unbounded label on every datapoint means a new series per session per metric. Anthropic's own note on the same page is the whole lesson in one sentence.
Lower cardinality generally means better performance and lower storage costs but less granular data for analysis.
Decide before you turn it on, not after. Per-user reporting: keep the user attributes, drop session.id. Org and team totals only: drop both and stamp team.id through OTEL_RESOURCE_ATTRIBUTES instead. And do not reach for user.id as your identity key.
Random anonymous identifier generated on first run and persisted in `~/.claude.json`. It contains no personal information and is not derived from your Claude account. Deleting the file produces a new unrelated value on next run.
The two configuration mistakes everybody makes
The first is silent and costs an afternoon: there is no default protocol, so an unset variable means nothing ships and nothing complains.
Claude Code has no default protocol, so set this or the signal-specific protocol variable for each `otlp` exporter you enable
The second is silent and costs you a wrong conclusion. Whatever you instrument inside a hook, an MCP server or a Bash tool call does not inherit the configuration, so a gap in your data is not necessarily a gap in the work.
Claude Code doesn't pass `OTEL_*` environment variables to the subprocesses it spawns, including the Bash tool, hooks, MCP servers, and language servers.
When building it yourself is the right call
Genuinely often. If any two of these describe you, close this page and go and set four environment variables.
- You already run a collector and a metrics store, so this is one more exporter and an afternoon.
- Claude Code is the only AI tool that matters to your org.
- You want the raw signal in your own warehouse, joined to CI, incidents and deploy data. No product's schema will beat your own joins for that, and it is not close.
- Your policy forbids third-party agents on engineer machines. That is a legitimate policy and no dashboard is worth arguing it away.
- Your fleet is not all macOS, which rules out this particular alternative anyway.
The OTel route is free, documented by the vendor, and gives you data nobody else holds a copy of. Those are real advantages and they do not go away because somebody has a product to sell.
When it stops being the cheap option
- Three tools instead of one. Cursor and Codex CLI have entirely different surfaces, so the second and third integrations are new work, not copies of the first.
- No MDM. Config distribution by request-and-hope has a long tail, and a machine that never got the config is indistinguishable from a machine that is idle.
- You need repository and branch attribution. None of the standard metric attributes carry either, so that join does not exist in the data you are collecting.
- You need to know when a credential goes into a prompt. Metrics will never tell you that, and the route that would (logging prompt content into your own pipeline) creates a bigger problem than it solves.
- The person maintaining it has another job. This is the failure mode that actually happens: it works for four months and then quietly stops being true.
What Synopt is, in OpenTelemetry terms
Synopt does not replace OpenTelemetry. It terminates it on the machine. The agent is an OTLP receiver bound to loopback, plus a normaliser, plus a shipper. Same protocol, different topology: instead of every laptop pushing to a collector you host, each laptop's telemetry is received, scanned, redacted and summarised where it was produced, and metadata goes up from there.
It also does job four, which is the actual reason it exists. The agent writes the same environment block into ~/.claude/settings.json that you would have written by hand, merges an [otel] table into ~/.codex/config.toml, and adds an entry to ~/.cursor/hooks.json, backing each file up first and refusing to touch a config whose shape it does not recognise.
The conflict to plan for: Claude Code exports to one endpoint per signal, and the agent takes ownership of those variables. If you already run a pipeline for Claude Code, decide which side owns the endpoint before you install, because both cannot have it.
Questions people actually ask
- Do I need an OpenTelemetry Collector to monitor Claude Code?
- No. You need something that accepts an OTLP push. A collector is the usual way to get one, and it is not the only way: a backend that ingests OTLP directly works, and so does a receiver running on the machine itself.
- Can I send Claude Code telemetry straight into my metrics store?
- Only if that store accepts OTLP over the wire, so check its docs before you plan around it. Whichever way you go, set OTEL_EXPORTER_OTLP_PROTOCOL explicitly: Claude Code has no default protocol, so leaving it unset means nothing ships and nothing errors.
- How much does the do-it-yourself route cost?
- No licence, and real time. Budget an afternoon for the first machine, a few days for distribution and a dashboard, then a recurring slice of somebody's month for drift, upgrades and the cardinality you did not plan for. If you already run the platform, most of that is already paid.
- Will an OpenTelemetry stack show me secrets pasted into prompts?
- Not by default, because prompt content is redacted from Claude Code's telemetry unless you opt in. If you do opt in, you have copied every prompt into your log store, secrets included, with wider access and longer retention than the credential. Think that through before you set the variable.
- Can I run Synopt and my own OpenTelemetry pipeline at the same time?
- Not for the same tool on the same machine, today. Claude Code exports to one endpoint per signal, and the agent takes ownership of those variables in ~/.claude/settings.json. The file is backed up first, so the decision is reversible, but it is a decision.
If job four is the one you do not want, that is the job Synopt is: an installer per Mac instead of a pipeline per team. Start a 14-day trial
Sources
- "Claude Code has no default protocol, so set this or the signal-specific protocol variable for each `otlp` exporter you enable" Anthropic, Claude Code docs: Monitoring usage. https://code.claude.com/docs/en/monitoring-usage Read 2026-09-08.
- "The following environment variables control which attributes are included in metrics to manage cardinality:" Anthropic, Claude Code docs: Monitoring usage (the Metrics cardinality control table, whose Default Value column reads true for OTEL_METRICS_INCLUDE_SESSION_ID, OTEL_METRICS_INCLUDE_ACCOUNT_UUID and OTEL_METRICS_INCLUDE_RESOURCE_ATTRIBUTES, and false for OTEL_METRICS_INCLUDE_VERSION and OTEL_METRICS_INCLUDE_ENTRYPOINT). https://code.claude.com/docs/en/monitoring-usage Read 2026-09-09.
- "Lower cardinality generally means better performance and lower storage costs but less granular data for analysis." Anthropic, Claude Code docs: Monitoring usage. https://code.claude.com/docs/en/monitoring-usage Read 2026-09-08.
- "Random anonymous identifier generated on first run and persisted in `~/.claude.json`. It contains no personal information and is not derived from your Claude account. Deleting the file produces a new unrelated value on next run." Anthropic, Claude Code docs: Monitoring usage (the user.id attribute). https://code.claude.com/docs/en/monitoring-usage Read 2026-09-08.
- "Claude Code doesn't pass `OTEL_*` environment variables to the subprocesses it spawns, including the Bash tool, hooks, MCP servers, and language servers." Anthropic, Claude Code docs: Monitoring usage. https://code.claude.com/docs/en/monitoring-usage Read 2026-09-08.
Read next
Published 2026-09-08. Last updated 2026-09-08.