Guide
How do you monitor Claude Code usage across a team?
Claude Code exports OpenTelemetry. Set CLAUDE_CODE_ENABLE_TELEMETRY=1 on each engineer's Mac, point the OTLP exporters at a collector you run, and you get session counts, token counts and claude_code.cost.usage per user. The hard part is not the metrics. It is getting that config onto every machine and keeping it there.
What Claude Code emits
Anthropic documents this in the open, so start there rather than with any vendor's summary of it. Claude Code speaks OpenTelemetry on all three signals: metrics, events over the logs protocol, and distributed traces, which are beta and off unless you ask for them. One environment variable turns the pipe on, and the exporter variables decide where it points.
Eight metrics come out. The names, descriptions and units below are Anthropic's own, copied from the docs linked at the foot of this page.
| Metric | What it counts | Unit |
|---|---|---|
| claude_code.session.count | Count of CLI sessions started | none |
| claude_code.token.usage | Number of tokens used | tokens |
| claude_code.cost.usage | Cost of the Claude Code session | USD |
| claude_code.lines_of_code.count | Count of lines of code modified | none |
| claude_code.commit.count | Number of git commits created | none |
| claude_code.pull_request.count | Number of pull requests created | none |
| claude_code.code_edit_tool.decision | Count of code editing tool permission decisions | none |
| claude_code.active_time.total | Total active time | s |
Two of those deserve a second look. claude_code.cost.usage is a counter in USD that Claude Code works out for itself, which makes it an API-equivalent estimate rather than a line on your Anthropic invoice. And claude_code.code_edit_tool.decision is the only raw material an acceptance rate can be built from: without it you are counting suggestions and calling it adoption.
How do you turn it on?
Three things have to be true on the machine: telemetry enabled, an exporter chosen for each signal you want, and somewhere for it to go.
- CLAUDE_CODE_ENABLE_TELEMETRY=1. Nothing leaves the machine without it.
- OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER. Normally otlp. Traces are a separate switch and stay off until you set one.
- OTEL_EXPORTER_OTLP_PROTOCOL. This is the step people skip, and skipping it is silent.
- OTEL_EXPORTER_OTLP_ENDPOINT. Wherever the thing that accepts OTLP is listening.
Claude Code has no default protocol, so set this or the signal-specific protocol variable for each `otlp` exporter you enable
Can you see usage per engineer?
Yes, once you know which identifier to join on. Every metric datapoint carries user.id, and an authenticated session also carries user.email, user.account_uuid, user.account_id and organization.id. The attribute that sounds the most useful is the one that is not.
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.
So user.email is your join key to an actual person, and it shows up only when the session authenticated over OAuth. Team structure is a separate problem: OTEL_RESOURCE_ATTRIBUTES stamps your own keys, team.id for instance, onto every datapoint, which beats maintaining a person-to-team lookup inside your BI tool. Each attribute you keep is also a cardinality decision, and Anthropic says so on the same page.
Lower cardinality generally means better performance and lower storage costs but less granular data for analysis.
What breaks when you roll this out to thirty Macs
Getting telemetry out of one machine takes an afternoon. The list below is what turns that afternoon into a standing commitment.
- Config distribution. These are environment variables, so they live in a shell profile or in ~/.claude/settings.json, on every machine, one machine at a time. Without MDM you are asking thirty people to paste a block into a dotfile, and the ones who did not paste it read as zero usage rather than as an error.
- Drift. Somebody reinstalls, somebody else resets their dotfiles, and their numbers quietly stop. Nothing tells you: an engineer who has stopped reporting looks exactly like an engineer who has stopped working.
- The subprocess gap, quoted below. Instrumenting what Claude Code spawns is a separate exercise.
- A destination that retains. OTLP has to land in something that speaks it and keeps it for as long as you plan to ask questions of it, which is a collector plus a metrics store plus somebody who owns both.
- No repository context. None of the standard metric attributes carry a repo or a branch, so "which repositories is this spend coming from" is not a question the metrics answer by themselves.
- One tool. This is Claude Code telemetry. Cursor and Codex CLI expose entirely different surfaces, and joining the three is your problem.
Claude Code doesn't pass `OTEL_*` environment variables to the subprocesses it spawns, including the Bash tool, hooks, MCP servers, and language servers.
When is do-it-yourself the right call?
If you already run a collector and a metrics store, and Claude Code is the only tool your team uses, stop reading and go set four environment variables. The OTel route is free, it is documented by the vendor, and it will do what you need. It stops being the cheap option at the point where you are packaging an installer, chasing the four Macs that never got the config, and writing the same dashboard a second time for Cursor.
How Synopt does it
Synopt is a macOS agent plus a hosted dashboard. On each Mac the agent runs an OTLP receiver bound to loopback and writes into ~/.claude/settings.json the same environment block you would have written by hand, pointed at 127.0.0.1. Claude Code exports to a process on the same machine, so nothing crosses a network boundary before something has read and scrubbed it.
The agent normalises those events, scans them for secrets and redacts anything it matches before the event is even buffered, then ships metadata to the dashboard. It does the same job for Codex CLI (an [otel] table in ~/.codex/config.toml) and for Cursor (an entry in ~/.cursor/hooks.json), so all three tools arrive in one place under one set of teams.
Stored per event: which tool and which model, timestamps, token counts, an estimated cost as reported by the tool's own telemetry, the repository, branch and project, and a secret-detection verdict (type, severity, and which field, never the value). Not stored: prompt text, which is off by default and whose default fails closed, the secrets themselves, source code, diffs, keystrokes, screenshots, browser history.
Two limits, up front rather than in a footnote. There is no alerting of any kind: findings appear in the dashboard and nothing notifies you. And the agent is macOS only, so an engineer on Linux or Windows is invisible to it.
Questions people actually ask
- Does Claude Code have a built-in team usage dashboard?
- No. Claude Code exports OpenTelemetry and stops there. The metrics are documented and stable enough to build on, but the dashboard is yours to build, to buy, or to point at a metrics backend you already run.
- Do I need an OpenTelemetry collector to monitor Claude Code?
- You need something that accepts OTLP. That can be a collector you run, a hosted backend that ingests OTLP directly, or an agent listening on the machine itself. What you cannot skip is the exporter configuration: Claude Code has no default protocol, so an unset OTEL_EXPORTER_OTLP_PROTOCOL means nothing ships and nothing complains.
- Can I see Claude Code usage per developer?
- Yes. Metric datapoints carry user.email when the session is authenticated over OAuth, alongside organization.id and two account identifiers. Do not join on user.id: Anthropic documents it as a random anonymous value generated on first run and stored in ~/.claude.json, and deleting that file produces a new unrelated one.
- Does Claude Code telemetry capture what people typed?
- Not by default. Anthropic's docs say prompt content is redacted by default and that OTEL_LOG_USER_PROMPTS=1 opts in. Turning it on is a real decision with real consequences for your DPO and, in Germany or Austria, for your works council.
- Does the telemetry cover hooks, MCP servers and Bash tool calls?
- No, and this one is easy to miss. Anthropic states that Claude Code does not pass OTEL_* environment variables to the subprocesses it spawns, including the Bash tool, hooks, MCP servers and language servers. Anything running inside those has to get its telemetry config from somewhere else.
Synopt does the endpoint half of this for Claude Code, Codex CLI and Cursor on macOS, with no collector to run and no dotfile to hand out. Start a 14-day trial
Sources
- "Cost of the Claude Code session" Anthropic, Claude Code docs: Monitoring usage (description of claude_code.cost.usage, unit USD). https://code.claude.com/docs/en/monitoring-usage Read 2026-09-08.
- "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.
- "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.
- "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.
- "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.
- "Prompt content. Redacted by default. Set `OTEL_LOG_USER_PROMPTS=1` to include it" 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.