Dash0 acquires Polar Signals

  • 15 min read

Codex vs. Claude Code: What Each One Lets You See

Many comparisons of these two agents turns into an argument about benchmarks, and the argument is less interesting than it looks. On Artificial Analysis' Terminal-Bench v2.1 run, GPT-5.6 Sol at xhigh effort scores 89.5% and Claude Opus 5 at max effort scores 89.1%. Vals AI, on a different harness, puts them at 85.77% and 84.64%, and has them tied at 84.44% on the hard split. Half a point, maybe three, depending on whose scaffold you trust and whether you count Opus 5's refusal fallbacks as passes.

What actually differs shows up after you hand one of these agents write access to a repository. Both Codex and Claude Code export OpenTelemetry over OTLP, no proprietary agent in the middle. Both then export almost completely different things. If you are the person who has to explain what the agents did last sprint and what it cost, that difference will shape your answer more than three benchmark points ever will.

Turning telemetry on

Claude Code reads its telemetry configuration from environment variables, using the standard OTEL_* names plus a few of its own. Nothing is exported until you set the enable flag.

bash
12345
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

Claude Code has no default OTLP protocol, so leaving OTEL_EXPORTER_OTLP_PROTOCOL unset is the most common reason nothing arrives. Traces are separate and still in beta: you also need CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 and OTEL_TRACES_EXPORTER. To check the wiring, start a session and look for claude_code.session.count in your backend.

Codex puts the same decision in an [otel] table in ~/.codex/config.toml, with a separate exporter for logs and traces.

toml
12345
[otel]
environment = "staging"
log_user_prompt = false
exporter = { otlp-grpc = { endpoint = "https://otel.example.com:4317" } }
trace_exporter = { otlp-grpc = { endpoint = "https://otel.example.com:4317" } }

The default for exporter and trace_exporter is none, which records events locally and sends nothing. metrics_exporter is a separate key, though, and it defaults to statsig, so anonymized usage metrics reach OpenAI unless you turn that off explicitly. One detail worth knowing before you plan a rollout: Codex ignores otel when it appears in a project-local .codex/config.toml. A repository you clone cannot redirect your agent's telemetry somewhere else, which is a good default and also means every path to enabling it runs through user-level or managed config.

Claude Code counts outcomes, Codex counts the runtime

This is the split that surprised me. Claude Code exports eight metrics, and seven of them are about work product or money:

MetricWhat it counts
claude_code.session.countSessions started
claude_code.lines_of_code.countLines added and removed, by model
claude_code.commit.countGit commits created
claude_code.pull_request.countPull requests opened
claude_code.cost.usageEstimated spend, in USD
claude_code.token.usageTokens, by input, output, and cache type
claude_code.code_edit_tool.decisionEdit permissions accepted or rejected
claude_code.active_time.totalActive time, excluding idle

The cost and token counters carry attributes for agent.name, skill.name, plugin.name, and mcp_tool.name, so spend attributes down to the subagent or skill that spent it. That is a chargeback story, and it is the kind of metric set someone designed after being asked "what did we get for the money" one too many times.

Codex publishes a far longer catalog, and it is a different kind of thing entirely. There is turn.ttft.duration_ms and turn.ttfm.duration_ms, sse_event.duration_ms, api_request.duration_ms, responses_api_engine_service_tbt.duration_ms, and tool.call.duration_ms. On latency instrumentation Codex is clearly ahead. If you want histograms of time to first token per turn, Codex hands them to you and Claude Code makes you derive them from spans.

Codex does emit a raw cost figure, turn.cost_microusd, alongside turn.token_usage by type. What it doesn't give you is the chargeback view: no breakdown by agent, skill, or MCP tool the way Claude Code's cost and token counters carry it. It also emits no commits, no pull requests, and no lines changed. Ask "what did the agent ship this week" and one tool answers with a metric while the other answers with a research project.

Read the two catalogs side by side and you can guess who each one was written for. Codex's telemetry looks like the product team's own service instrumentation, exposed to you because it was already there. Claude Code's looks like it was built for a platform team that has to report upward. Neither is wrong. They answer different questions, and it is worth knowing which question you actually have before you build the dashboard.

Trace context is the real gap

Metrics tell you the aggregate. Traces tell you what happened in one run, and this is where the two diverge most.

Claude Code builds a span tree per prompt. A claude_code.interaction root span, with claude_code.llm_request and claude_code.tool as children, and each tool span split into the time blocked on a permission decision and the time actually executing. When a subagent runs, its spans nest under the parent's tool span, so a fan-out is legible instead of being a pile of unrelated sessions.

The part that matters for anyone running production systems is what happens at the edges of that tree. When tracing is active, Claude Code injects TRACEPARENT into the Bash and PowerShell subprocesses it spawns. Any script the agent runs that reads W3C trace context parents its own spans under the tool execution span. The migration your agent just ran, the test suite it kicked off, the deploy script it invoked: all of it can land in the same trace as the decision to run it. In the other direction, Agent SDK and claude -p sessions read an inbound TRACEPARENT, so an agent invoked from CI appears as a child of the pipeline's trace rather than a disconnected island. Claude Code also sends traceparent on requests to the Anthropic API and records the returned traceresponse as a span link.

Codex has a trace exporter and its events carry a conversation id, a model, and the sandbox and approval settings for the run. Its docs describe no equivalent propagation into spawned commands. So you can see that Codex called apply_patch and how long it took. Correlating that with what the patched service did in production is manual work, joined on timestamps and hope.

If your reason for instrumenting a coding agent is governance and spend, this gap is a footnote. If your reason is understanding why an agent-authored change broke something at 3am, it is the whole thing.

Neither one follows the GenAI conventions

The OpenTelemetry GenAI semantic conventions now model an agent run as a span tree, with invoke_agent at the root, chat for each model call, and execute_tool underneath. That is a close match for what both of these agents actually do. Every gen_ai.* attribute in the registry is still marked Development, and the conventions moved out of the main semantic conventions repository into their own, which is part of why so many blog posts get their status wrong.

Claude Code hedges. It emits its own attribute names and adds a small set of convention aliases alongside them: gen_ai.system, gen_ai.request.model, gen_ai.response.id, gen_ai.response.finish_reasons, gen_ai.tool.call.id. Codex namespaces everything under codex.* and leaves it there.

Practically, this means a dashboard built on one agent's metric names does not transfer to the other, and neither set will survive the conventions going stable untouched. Normalize at the collector. A transform processor that maps both vocabularies onto the GenAI names costs an afternoon and saves you rewriting every query later.

Claude Code has a shortcut here. The Dash0 agent plugin emits gen_ai.* and vcs.* spans that already follow the conventions, so the mapping happens before the data leaves the machine. For Codex you are writing the transform yourself.

Who controls the exporter

Both agents redact prompt content by default, which is the right call and worth verifying rather than assuming.

Claude Code's controls are granular to the point of being fussy: separate flags for user prompts, assistant responses, tool parameters, tool content, and raw API bodies, plus a content length cap and a set of OTEL_METRICS_INCLUDE_* switches for cardinality. Third-party plugin and skill names are replaced with placeholders by default, which is a thoughtful default I have not seen elsewhere. For fleet rollouts, managed settings actively strip developer-set endpoint and credential variables at startup, so an organization can pin the collector and a developer cannot quietly point one signal somewhere else.

Codex gives you log_user_prompt for content, an [analytics] enabled = false switch for the anonymous usage data it sends back to OpenAI by default, and the project-config restriction described earlier. Fewer knobs, and the ones that matter are there.

Whichever agent you run, decide whether redaction happens at the source or on ingestion before you roll anything out to a team. Source redaction means the content never leaves the developer's machine. Ingestion redaction means you can change your mind without touching every laptop. Dash0 does both: omit_io and omit_user_info in the plugin, and GenAI Attribute Redaction as a per-dataset setting.

If you run both

Plenty of teams are not choosing. They run Codex for parallel, well-specified work and Claude Code for the changes that need supervision. If that is you, a few things save pain:

Send both to a collector rather than straight to a backend, and do the vocabulary mapping there. Decide early whether you are measuring spend or outcomes, because the two agents make opposite assumptions and a dashboard that hedges will be missing half its panels. Key correlation on Claude Code's session.id and prompt.id and Codex's conversation id, and accept that joining across agents means joining on the repository and the commit.

Darkplane, Dash0's control room for AI-assisted development, covers the Claude Code side of this today through AI Coding Insights. Claude Code's native OTLP stream is enough on its own to populate cost and adoption. The open-source Dash0 agent plugin adds tool calls, subagent activity, and full conversation replay, and stamps every span with repository, branch, commit, and pull-request URL. Connect GitHub alongside it and the loop closes in a way neither agent manages alone: session, to commit, to merged PR, with cycle time attached.

Given everything above, the tab worth opening first is Analyze Tools & Skills. The per-skill, per-subagent, and per-MCP-server attribution that Claude Code hangs off its cost and token counters is exactly what it reads, so "which of our skills is eating the budget" stops being a query you write. Measure Productivity handles the other half, tracking assisted PRs and p90 cycle time against the previous period. Cost across all of it comes from token counts priced against a Dash0-maintained catalog at API-equivalent rates rather than from your invoice, which matters because a subscription plan's billing dashboard reports most of this consumption as zero. Cursor is supported alongside Claude Code, and since the views group by model rather than by agent vendor, the two compare on the same terms. Codex is not there yet, though more agents are on the roadmap.

If you want to see the raw telemetry first, the Claude Code monitoring guide walks through a local Docker Compose stack: an OpenTelemetry Collector routing to Prometheus and Perses for metrics, Jaeger for traces, and Data Prepper into OpenSearch for logs.

Final thoughts

Codex and Claude Code are close enough on capability that picking between them on benchmark scores is close to picking at random. The operational differences are real and they are asymmetric: Claude Code gives you outcome metrics, cost in dollars, and trace context that reaches into the processes the agent spawns. Codex gives you better latency instrumentation and a cleaner story about what a repository is allowed to reconfigure.

Whichever you run, turn the telemetry on before you need it, and put a collector in the middle so you can change your mind later. The conventions are still moving, both vendors are shipping weekly, and the only part of this that stays stable is OTLP.

Dash0 is an OpenTelemetry-native observability platform, and Darkplane is the part of it pointed at the code your agents write before that code ships. Start a free trial and you can have Claude Code sessions, token spend, and tool-call spans sitting next to the production telemetry from the services those sessions changed. Fourteen days, no credit card.

    Related Reads