Skip to content

Claude Code

Claude Code has two collection surfaces: native OTLP (metrics, log events, and — on Claude Code ≥ 2.1.x — traces, emitted directly when the right environment variables are set) and session transcripts (the append-only JSONL Claude Code writes per session to ~/.claude/projects/, scraped by the collector’s file_log receiver). Native OTLP is the preferred, better-documented path; transcripts carry richer conversational detail at the cost of an undocumented, drifting schema.

What it collects

  • Metrics — session count, lines of code added/removed, PRs, commits, cost and token usage by model, tool-edit decisions, active time.
  • Log events — user_prompt, tool_result, api_request, api_error, tool_decision, hook_execution_complete, mcp_server_connection, subagent_completed, skill_activated, plugin_loaded, and more.
  • Traces (Claude Code ≥ 2.1.x) — an interaction span per prompt-to-response cycle, an llm_request span per model call underneath it, and a tool span per dispatch with its execution and permission-wait children.
  • Transcripts — every user/assistant/tool message for every session.

What you get

The pack installs a semantic layer over all four sources plus a set of routed dashboards, at @frames/@claude_code/:

RouteWhat it answers
@claude_codeA hub whose sections are routes: Overview (spend, tokens, rhythm), Usage (where the money goes, by model / effort / skill / request class), Work (people, sessions, skills, MCP servers, tools, commits, PRs, lines), Reliability (latency, TTFT, failures, API errors)
sessions / sessions/{SessionId}Every session, and one session sectioned into conversation, usage, tools, reliability and a span timeline — each at its own URL
users / users/{UserEmail}Everyone with telemetry, and one person’s spend, surfaces, sessions and tools
tools / tools/{ToolName}Tool calls, failure rates and durations
skills / skills/{SkillName}What a skill dispatches, and the spend attributed to it
hooks / hooks/{HookName}Hook runs, duration and how each is registered
agents / agents/{AgentName}Sub-agent runs, spend by model, tool uses
mcp/servers/…MCP servers, their connection health, and the tools each exposes

@frames/@claude_code/README.md is the data reference — every stream, scalar and measure, with the coverage figures and caveats behind them. Panels live in *.templates.json files beside each page, so a *.templates.overrides.json retunes one panel without forking the page (see pages and templates). The hub and the session page declare their parts as sections, so each is its own URL and noemata validate --online renders all of them.

What Noemata does automatically

Enabling this integration writes an env block into ~/.claude/settings.json (turning on telemetry, pointing it at Noemata’s collector, and — on Claude Code ≥ 2.1.x — enabling the trace SDK), and configures the collector to receive it. Because ~/.claude/settings.json is outside the repository, this write only happens with integrations.external_edits on; setup asks once and records a decline.

What you still have to do

  • Open a new terminal after enabling — Claude Code reads settings.json’s env block at startup, so a session already running won’t pick up the change.
  • Check for a managed-settings file. If your machine has /Library/Application Support/ClaudeCode/managed-settings.json (macOS) or /etc/claude-code/managed-settings.json (Linux) with its own env block, that overrides Noemata’s edit silently — telemetry won’t flow and nothing will tell you why.
  • Transcript history follows Claude Code’s own retention (cleanupPeriodDays, 30 by default). Archive externally if you want a longer window than that.
  • user.email appears on OAuth-authenticated sessions: Claude Code stamps it on native telemetry, and reconcile stamps it on transcripts from ~/.claude.json (with user.id, user.account_uuid, organization.id), which also puts it in the generated, gitignored config/collector/collector.yml. A re-login lands at the next reconcile. It’s identity rather than conversation content, so redact_content doesn’t remove it — scrub it downstream if that’s a concern.

What redact_content does

By default (redact_content: false), the content gates are open: prompts, assistant responses, and tool inputs/results are sent. Setting redact_content: true closes them, in two places:

  • At the source — the env vars that gate prompt, response, and tool-content text (OTEL_LOG_USER_PROMPTS, OTEL_LOG_ASSISTANT_RESPONSES, OTEL_LOG_RAW_API_BODIES, and — on Claude Code ≥ 2.1.x, where tool input/output is emitted as span events — OTEL_LOG_TOOL_CONTENT) are turned off, so that content is never emitted in the first place.
  • In the collector — everything else (tool inputs and results on versions with no trace SDK, transcript message content) is stripped by an explicit allowlist of attribute keys, applied to every native-OTLP and transcript record. What survives: identity, session structure, timings, token/cost accounting, and tool/skill/MCP-server names; their inputs, their outputs, and the conversation itself are always stripped.

This only holds for a collector Noemata owns. The allowlist is enforced in the collector, so with collect.collector: external — a collector this project doesn’t generate config for — reconcile warns that it can’t apply, and content reaches whatever that collector exports to. The source-level gates above still apply regardless.

Settings

{
"integrations": {
"installed": {
"claude_code": {
"redact_content": false,
"hooks": { "validate_frames_on_edit": "offline" }
}
}
}
}
  • redact_content — see above. Defaults to false.
  • hooks.validate_frames_on_edit — offline (default), online, or none. Installs a PostToolUse hook that re-validates the frames an edit invalidates. See Validating a frame.

Further reading