OpenTelemetry and Analytics for Claude Code, Codex and Cursor
Coding-agent telemetry comes in three layers: OpenTelemetry export to a collector you run, vendor analytics dashboards for adoption and pull request attribution, and per-commit provenance from Entire CLI. The claude.ai dashboard shows no per-user cost; that comes from OpenTelemetry, the spend report or, on Enterprise, the Analytics API. Cursor had no verified OpenTelemetry export on 2026-09-26.
This page is for the developer who wants to see what their agent sessions cost and do, and the tech lead who has to answer “is the agent rollout working?” at the next planning meeting. You have 12 seats, an invoice, and a dashboard that says 70% of people were active last week. Nobody can tell you which models the money went to, which merged pull requests the agents wrote, or whether those changes survived.
The canonical page for production telemetry pipelines, audit retention and join keys is observing the agents. The observability and quality overview places this page among the cost, review, security and eval tools. This page is the tool shelf: what each product emits, a local stack you start with one command, and the monthly adoption review built on it.
What this telemetry setup gives you
Section titled “What this telemetry setup gives you”- A working local stack: Claude Code and Codex exporting OpenTelemetry into Grafana on your laptop, verified with a single query.
- PromQL for the six Claude Code panels a tech lead asks for: cost by model, tokens by type, active users, sessions, edit accept rate, and commits and pull requests.
- A clear map of what the Claude Code analytics dashboard shows, what it hides (per-user cost), and when it goes dark (Zero Data Retention).
- Entire CLI set up per tool, so any commit leads back to the prompts and session that produced it.
- A monthly adoption review in four steps, and three copy-paste prompts that automate it.
Which telemetry does each tool give you?
Section titled “Which telemetry does each tool give you?”Start by knowing which layer answers which question. Telemetry answers “what did the sessions do and cost?”; analytics answers “who uses it and how much merged code came from it?”; provenance answers “why was this commit written this way?”.
| Layer | Claude Code 2.1.283 | Codex CLI 0.157.1 | Cursor |
|---|---|---|---|
| OpenTelemetry export | Metrics and log events; traces in beta (CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1) | Logs, traces and metrics from [otel] in config.toml | None verified on 2026-09-26 |
| Cost signal | claude_code.cost.usage (USD) with model, effort, query_source | codex.turn.cost_microusd | Not verified |
| Delivery signal | claude_code.commit.count, claude_code.pull_request.count, claude_code.lines_of_code.count | No commit or pull request metric in codex-rs/otel at rust-v0.157.1 | Not verified |
| Vendor dashboard | claude.ai/analytics/claude-code (Team, Enterprise); platform.claude.com/claude-code (Console) | Enterprise analytics dashboard and Analytics API (secondary: OpenAI help pages seen only in search extracts) | Team/Enterprise analytics dashboard and Analytics API; AI Code Tracking API (Enterprise only) maps AI-generated lines to commits (secondary: cursor.com docs seen only in search extracts; endpoints unverified) |
| Per-session view on your machine | /usage (/cost and /stats are aliases); /insights writes an HTML report on recent sessions | codex exec --json events per run | Not verified |
| Commit provenance | Entire CLI (entire enable --agent claude-code) | Entire CLI (--agent codex) | Entire CLI (--agent cursor) |
Sources: Claude Code monitoring and analytics docs; Codex source (codex-rs/config/src/types.rs and codex-rs/otel/src/metrics/names.rs at rust-v0.157.1); the entireio/cli README; all read 2026-09-26.
The shared OpenTelemetry vocabulary for agents (invoke_agent, execute_tool) lives in open-telemetry/semantic-conventions-genai and was still Status: Development on 2026-09-26, so each tool uses its own names. Build dashboards per tool now and expect renames later.
What does the Claude Code analytics dashboard show, and what does it hide?
Section titled “What does the Claude Code analytics dashboard show, and what does it hide?”The Team and Enterprise dashboard at claude.ai/analytics/claude-code is the fastest adoption view, and it has four limits that change how you use it:
- No per-user cost. It shows lines accepted, suggestion accept rate, daily active users, sessions, contribution metrics, and a top-10 leaderboard with a CSV export of all users. Per-user tokens and cost come from OpenTelemetry or from the spend report in your organization’s analytics settings.
- Contribution metrics need GitHub and are a public beta. An Owner enables analytics and the GitHub analytics toggle at
claude.ai/admin-settings/claude-code, and a GitHub admin installs the app atgithub.com/apps/claude. Data typically appears within 24 hours. Only users in your claude.ai organization count; Console API usage does not. - Zero Data Retention turns contribution metrics off. A ZDR organization sees usage metrics only, so pull request attribution has to come from your own labels or Entire trailers.
- Attribution is deliberately conservative. A merged pull request counts when Claude Code sessions from 21 days before to 2 days after the merge match its added lines; code rewritten by more than 20% is not attributed, and lock files, build output and snapshots are excluded. Credited pull requests get the GitHub label
claude-code-assisted.
That label is the cheapest attribution query you have. In GitHub search, is:pr is:merged label:claude-code-assisted lists every merged pull request the engine credited, without API access. On Enterprise, the Claude Enterprise Analytics API returns per-user engagement, usage and cost reports with a key that has the read:analytics scope; the Team plan does not get the API. Console customers use the Claude Code Analytics API with an Admin API key, and the Console notes that its spend figures are estimates, not billing.
Send Claude Code and Codex telemetry to a local Grafana stack
Section titled “Send Claude Code and Codex telemetry to a local Grafana stack”This is the full example: one container, two agents, one query that proves data arrives. It runs on your laptop and costs nothing but disk. The grafana/otel-lgtm image bundles an OpenTelemetry Collector, Prometheus, Loki, Tempo, Pyroscope and Grafana; Grafana describes it as intended for development, demo and testing, not production.
-
Start the stack. In a terminal:
Terminal window docker run --rm --name lgtm \-p 3000:3000 -p 4317:4317 -p 4318:4318 -p 9090:9090 \grafana/otel-lgtmPort 3000 is Grafana (log in as
admin/admin), 4317 is OTLP over gRPC, 4318 is OTLP over HTTP, and 9090 is Prometheus, which the prompts below query directly. -
Point each agent at the collector. The settings differ per tool.
Export the variables in the shell that starts
claude, or put the same keys underenvin~/.claude/settings.json. Claude Code ignores exporter variables in a repository’s.claude/settings.jsonand.claude/settings.local.json, so a repository cannot turn telemetry on or redirect it.Terminal window export CLAUDE_CODE_ENABLE_TELEMETRY=1export OTEL_METRICS_EXPORTER=otlpexport OTEL_LOGS_EXPORTER=otlpexport OTEL_EXPORTER_OTLP_PROTOCOL=grpcexport OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulativeexport OTEL_METRIC_EXPORT_INTERVAL=10000export OTEL_METRICS_INCLUDE_REPOSITORY=trueclaudeClaude Code has no default OTLP protocol, so
OTEL_EXPORTER_OTLP_PROTOCOLis required. Temporality defaults todelta; the docs say to switch tocumulativewhen the backend needs it, and a Prometheus-backed stack stores cumulative counters. The 10-second interval is for testing; the default is 60 seconds.OTEL_METRICS_INCLUDE_REPOSITORY=true(v2.1.269 and later) addsvcs.repository.nameand related attributes, so you can filter by repository.Prompt text, tool input and responses stay redacted unless you set
OTEL_LOG_USER_PROMPTS,OTEL_LOG_TOOL_DETAILSorOTEL_LOG_ASSISTANT_RESPONSES. Leave them off here.Add an
[otel]table to~/.codex/config.toml. The field names come fromOtelConfigTomlin the Codex source, and this block loads without error in codex-cli 0.157.1. A misspelled key name is silently ignored in 0.157.1 and leaves that exporter at its default; a misspelled exporter name such asotlp-grcpfails at startup withError loading config.toml: unknown variant:[otel]environment = "dev"log_user_prompt = falseexporter = { otlp-grpc = { endpoint = "http://localhost:4317" } }trace_exporter = { otlp-grpc = { endpoint = "http://localhost:4317" } }metrics_exporter = { otlp-grpc = { endpoint = "http://localhost:4317" } }Set all three exporters. In 0.157.1 the log and trace exporters default to
noneandmetrics_exporterdefaults tostatsig, a Codex-internal destination. For an HTTP collector, useotlp-http = { endpoint = "...", protocol = "binary" }with the full signal path (for examplehttp://localhost:4318/v1/logs). Headers for an authenticated collector go in an inlineheaders = { ... }table in plain text, so write that file from a secret store and never commit it.Metric names to look for include
codex.thread.started,codex.turn.token_usage,codex.turn.cost_microusd,codex.tool.callandcodex.api_request.No OpenTelemetry export for Cursor could be verified on 2026-09-26, so Cursor does not send anything to this stack. Two verified routes give you part of the picture:
- Entire CLI records Cursor sessions per commit through
.cursor/hooks.json(see trace a commit back to its session below). - Cursor hooks receive agent events as JSON over stdio (verified 2026-08-28), so a hook can append events to a file your collector tails. The event names were not verified; check Cursor’s hooks docs before you build panels on them.
For team usage and spend, Cursor has a Team/Enterprise analytics dashboard and Analytics API, and an AI Code Tracking API (Enterprise only) that maps AI-generated lines to commits (secondary: cursor.com docs seen only in search extracts; endpoints unverified). Check what your plan actually exports before you promise anyone a number.
- Entire CLI records Cursor sessions per commit through
-
Prove data arrives. Run one short session in each agent, then open Grafana at
http://localhost:3000, go to Explore, pick the Prometheus data source, and run:{__name__=~"claude_code_session_count.*|codex_thread_started.*"}You should see one series per session start within about 10 seconds for Claude Code. Dots in OpenTelemetry names become underscores in Prometheus, and Prometheus can append a unit or
_totalsuffix, which is why every query here matches on a name prefix. For events, pick the Loki data source and search foruser_prompt. If nothing arrives, runclaude --debug-file /tmp/claude-otel.logand search that file for[3P telemetry]; lines tagged[Anthropic telemetry]are Anthropic’s own operational telemetry and do not indicate a problem. -
Build the six Claude Code panels. Create a dashboard with one panel per query, each over a 7-day range:
# Cost by model (USD, client-side estimate)sum by (model) (increase({__name__=~"claude_code_cost_usage.*"}[7d]))# Tokens by type: input, output, cacheRead, cacheCreationsum by (type) (increase({__name__=~"claude_code_token_usage.*"}[7d]))# Active users (count only; never chart per person)count(count by (user_email) (increase({__name__=~"claude_code_session_count.*"}[7d]) > 0))# Sessions by start type: fresh, resume, continue, agents_viewsum by (start_type) (increase({__name__=~"claude_code_session_count.*"}[7d]))# Edit accept rate: Edit, Write, NotebookEdit decisionssum(increase({__name__=~"claude_code_code_edit_tool_decision.*", decision="accept"}[7d]))/ sum(increase({__name__=~"claude_code_code_edit_tool_decision.*"}[7d]))# Commits and pull requests the agent createdsum(increase({__name__=~"claude_code_commit_count.*|claude_code_pull_request_count.*"}[7d]))The accept-rate panel counts every decision, including edits that settings, the permission mode or a hook accepted without a person (
source="config"orsource="hook"), so in permissive modes it drifts toward 100%. For a human accept rate, addsource=~"user_.*"to both the numerator and the denominator.With per-session series,
increase()misses each series’ first sample, so short sessions undercount. For totals you reconcile against the invoice, usesum by (model) (max_over_time({__name__=~"claude_code_cost_usage.*"}[7d])), or setOTEL_METRICS_INCLUDE_SESSION_ID=falsefor dashboard metrics.If a label such as
user_emailis missing, open the metric in Explore and use the label name your stack shows. Add Codex panels the same way oncecodex_series appear. -
Move from laptop to team. When the panels answer real questions, replace the container with a production collector and push the Claude Code variables through managed settings, which override developer-set exporter variables. For a rotating collector token, point
otelHeadersHelperin settings at a script that prints the headers as JSON; Claude Code reruns it every 29 minutes by default. The production pipeline (pseudonymized analytics stream, identified audit stream, retention) is on the agent observability page.
Two open reference stacks save you the dashboard work if you want more than six panels. As of 2026-09-26 (GitHub stars, read through the GitHub API for the site’s research catalogue): ColeMurray/claude-code-otel (506 stars) is a Docker Compose stack of collector, Prometheus, Loki and Grafana with prebuilt cost, tool, user and session dashboards (make up); anthropics/claude-code-monitoring-guide (370 stars) is Anthropic’s ROI guide with a docker-compose.yml, otel-collector-config.yaml, Grafana dashboards and a report-generation prompt. Neither covers Codex.
Trace a commit back to its session with Entire CLI
Section titled “Trace a commit back to its session with Entire CLI”Metrics tell you that 40 commits came from agent sessions last week. They do not tell you why a specific line in billing/retry.ts looks the way it does. Entire CLI (Entire Inc., MIT, entireio/cli) fills that gap: it installs git hooks plus per-agent hooks, captures prompts, transcript, tool calls, files touched and token usage while the agent works, and on commit condenses them into a checkpoint linked by an Entire-Checkpoint: <id> commit trailer. Entire never creates commits on your branch.
Popularity as of 2026-09-26: 5.1k GitHub stars; latest stable v0.11.3, published 2026-09-25 (GitHub API and releases page). It is a Go binary; the npm package entire is an unrelated project.
brew install --cask entireio/tap/entirecd your-projectentire enable --agent claude-code # hooks land in .claude/settings.jsonentire statusbrew install --cask entireio/tap/entirecd your-projectentire enable --agent codex # hooks land in .codex/hooks.jsonentire statusbrew install --cask entireio/tap/entirecd your-projectentire enable --agent cursor # hooks land in .cursor/hooks.jsonentire statusAdd a second agent later with entire agent add codex. Team settings go in .entire/settings.json; personal overrides go in the gitignored .entire/settings.local.json. Other install routes in the README are an install script, Scoop on Windows and go install.
Reviewing an agent-written pull request then looks like this:
git log -1 --format=%B # message ends with: Entire-Checkpoint: <id>entire checkpoint explain <CHECKPOINT_ID> # prompts, transcript, files touched, tokensentire search "rate limiter retry" # search across checkpoints, commits and sessionsCHECKPOINT_ID is the value after Entire-Checkpoint: in the commit message. Checkpoints are stored as git refs under refs/entire/checkpoints/ and pushed with your normal git push; the older single entire/checkpoints/v1 branch is the legacy backend.
Run the monthly adoption review: usage, attribution, outcomes
Section titled “Run the monthly adoption review: usage, attribution, outcomes”Telemetry pays off only when it feeds a decision. The review below runs monthly and is owned by the tech lead. It follows the utilization, impact and cost split that DX’s AI Measurement Framework uses (secondary: DX research page seen in a search extract), with the metric definitions from the site’s metrics frameworks page.
-
Usage: who uses which agent, on which models, at what cost. Pull active users, sessions and cost by model from the Grafana panels (Claude Code and Codex) and from the vendor dashboards (all three tools). Compare active users with paid seats. Report cost by model and by repository, never by person.
-
Attribution: which merged changes involved an agent. Combine three sources, strongest first: your own
agent-assistedpull request label or template checkbox, theclaude-code-assistedlabel from the Claude Code dashboard, andEntire-Checkpointtrailers in merged commits. A pull request with any of them counts as agent-assisted. The vendor label alone undercounts, because it ignores Codex and Cursor and is deliberately conservative. -
Outcomes: did those changes survive. For the agent-assisted cohort and the rest, compute accepted change rate, 14-day follow-up fix rate and review time, as defined on the metrics page. Divide the month’s agent spend (seats plus usage) by accepted agent-assisted changes to get cost per accepted change.
-
Decide and record. Pick at most two changes for next month (a model switch, a context file fix, a training session for a repository with low adoption) and write down which number should move. The tech lead signs off the review; the next review checks whether the number moved.
How do you know the telemetry numbers are right?
Section titled “How do you know the telemetry numbers are right?”A dashboard nobody has checked is an opinion. Verify it once when you set it up, then monthly, without reading any agent diff:
- Reconcile cost with the invoice.
claude_code.cost.usageandcodex.turn.cost_microusdare computed on the client, and the Console states that its own spend figures are estimates. Compare the month’s telemetry total with the invoice; the invoice is the number you report, and telemetry is the breakdown. - Smoke-test every machine image. One session per laptop image and CI image, followed by the step 3 query. A CI image without the variables is a silent hole in every total.
- Spot-check attribution. Take five merged pull requests: confirm each label or Entire trailer against the session that produced it. Record the error rate on the review page.
- Alert on silence. A Grafana alert on “no
claude_code_session_countdata for 24 hours on a working day” catches a broken exporter before the monthly review does. - Keep identity out of analytics. The review counts users; it never ranks them. The career ladders page explains why per-person token or PR counts turn into targets people game.
What breaks when you collect agent telemetry, and how to recover
Section titled “What breaks when you collect agent telemetry, and how to recover”Claude Code runs but no metrics arrive. The usual cause is a missing OTEL_EXPORTER_OTLP_PROTOCOL or variables placed in project settings. Recovery: set the protocol, move the variables to the shell, user settings or managed settings, and check the debug file for [3P telemetry] errors.
Metrics arrive but the panels stay empty or jump. Delta temporality against a cumulative backend. Recovery: set OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative and restart the session.
Codex telemetry is missing. Either a key name is misspelled (Codex ignores it silently) or [otel] sits in another CODEX_HOME than the one the session reads. Recovery: confirm CODEX_HOME, then diff your keys against exporter, trace_exporter and metrics_exporter. A reported issue (openai/codex#12913, status unverified on 0.157.1) says codex exec emits no OpenTelemetry metrics, so confirm headless runs separately before you build panels on them.
Prometheus storage grows fast. session.id is on every Claude Code metric by default, so each session is a new series. Recovery: in a large team, set OTEL_METRICS_INCLUDE_SESSION_ID=false and keep session IDs on log events, where cardinality is cheap.
The adoption review shows Claude Code winning and Codex invisible. The vendor label only credits Claude Code, and Codex 0.157.1 emits no commit metric. Recovery: attribute through your own agent-assisted label and Entire trailers, which cover all three tools.
A secret appears in the log store. Someone turned on OTEL_LOG_TOOL_DETAILS or OTEL_LOG_USER_PROMPTS. Recovery: rotate the secret, purge the records, and route content only to the audit pipeline described on the agent observability page.
PR attribution disappears after a contract change. Zero Data Retention switches contribution metrics off. Recovery: keep your own label and Entire trailers as the primary attribution, so the review does not depend on one vendor feature.
Where to go next with agent telemetry
Section titled “Where to go next with agent telemetry”Frequently asked questions
Does the Claude Code analytics dashboard show cost per user?
No. The Team and Enterprise dashboard shows usage, contribution metrics and a leaderboard. For per-user tokens and cost, export OpenTelemetry to your own collector, download the spend report from the organization's analytics settings or, on Enterprise, call the Analytics API.
How do I turn on OpenTelemetry in Claude Code?
Set CLAUDE_CODE_ENABLE_TELEMETRY=1 plus OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER, and set OTEL_EXPORTER_OTLP_PROTOCOL because Claude Code has no default protocol. Variables in a repository's .claude/settings.json are ignored; use your shell, user settings or managed settings.
How do I export Codex telemetry?
Add an [otel] table to ~/.codex/config.toml with exporter, trace_exporter and metrics_exporter set to otlp-grpc or otlp-http. Set metrics_exporter explicitly, because in Codex CLI 0.157.1 it defaults to statsig.
Are PR attribution metrics available under zero data retention?
No. Organizations with Zero Data Retention see usage metrics only on the Claude Code dashboard; contribution metrics, including PR attribution, are unavailable. Use your own pull request labels or Entire CLI commit trailers instead.