claude_memory 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/memory.sqlite3 +0 -0
  3. data/.claude/rules/claude_memory.generated.md +42 -64
  4. data/.claude/skills/release/SKILL.md +44 -6
  5. data/.claude/skills/study-repo/SKILL.md +15 -0
  6. data/.claude-plugin/commands/audit-memory.md +68 -0
  7. data/.claude-plugin/marketplace.json +1 -1
  8. data/.claude-plugin/plugin.json +1 -1
  9. data/CHANGELOG.md +70 -0
  10. data/CLAUDE.md +20 -5
  11. data/README.md +64 -2
  12. data/db/migrations/018_add_otel_telemetry.rb +81 -0
  13. data/docs/1_0_punchlist.md +522 -89
  14. data/docs/GETTING_STARTED.md +3 -1
  15. data/docs/api_stability.md +341 -0
  16. data/docs/architecture.md +3 -3
  17. data/docs/audit_runbook.md +209 -0
  18. data/docs/claude_monitoring.md +956 -0
  19. data/docs/dashboard.md +23 -3
  20. data/docs/improvements.md +329 -5
  21. data/docs/influence/ai-memory-systems-2026.md +403 -0
  22. data/docs/memory_audit_2026-05-21.md +303 -0
  23. data/docs/plugin.md +1 -1
  24. data/docs/quality_review.md +35 -0
  25. data/lib/claude_memory/audit/checks.rb +239 -0
  26. data/lib/claude_memory/audit/finding.rb +33 -0
  27. data/lib/claude_memory/audit/runner.rb +73 -0
  28. data/lib/claude_memory/commands/audit_command.rb +117 -0
  29. data/lib/claude_memory/commands/dashboard_command.rb +2 -1
  30. data/lib/claude_memory/commands/digest_command.rb +95 -3
  31. data/lib/claude_memory/commands/hook_command.rb +27 -2
  32. data/lib/claude_memory/commands/import_auto_memory_command.rb +180 -0
  33. data/lib/claude_memory/commands/initializers/hooks_configurator.rb +7 -4
  34. data/lib/claude_memory/commands/otel_command.rb +240 -0
  35. data/lib/claude_memory/commands/registry.rb +5 -1
  36. data/lib/claude_memory/commands/show_command.rb +90 -0
  37. data/lib/claude_memory/commands/stats_command.rb +94 -2
  38. data/lib/claude_memory/configuration.rb +60 -0
  39. data/lib/claude_memory/core/fact_query_builder.rb +1 -0
  40. data/lib/claude_memory/dashboard/api.rb +8 -0
  41. data/lib/claude_memory/dashboard/index.html +140 -1
  42. data/lib/claude_memory/dashboard/prompt_journey.rb +48 -0
  43. data/lib/claude_memory/dashboard/server.rb +86 -0
  44. data/lib/claude_memory/dashboard/telemetry.rb +156 -0
  45. data/lib/claude_memory/dashboard/trust.rb +180 -11
  46. data/lib/claude_memory/deprecations.rb +106 -0
  47. data/lib/claude_memory/distill/bare_conclusion_detector.rb +71 -0
  48. data/lib/claude_memory/distill/reference_material_detector.rb +37 -4
  49. data/lib/claude_memory/hook/auto_memory_mirror.rb +7 -3
  50. data/lib/claude_memory/hook/context_injector.rb +11 -2
  51. data/lib/claude_memory/hook/handler.rb +142 -1
  52. data/lib/claude_memory/mcp/tool_definitions.rb +3 -3
  53. data/lib/claude_memory/otel/attributes.rb +118 -0
  54. data/lib/claude_memory/otel/constants.rb +32 -0
  55. data/lib/claude_memory/otel/ingestor.rb +54 -0
  56. data/lib/claude_memory/otel/otlp_json_envelope.rb +254 -0
  57. data/lib/claude_memory/otel/prompt_scope.rb +108 -0
  58. data/lib/claude_memory/otel/settings_writer.rb +122 -0
  59. data/lib/claude_memory/otel/status.rb +58 -0
  60. data/lib/claude_memory/recall/staleness_annotator.rb +73 -0
  61. data/lib/claude_memory/resolve/predicate_policy.rb +17 -1
  62. data/lib/claude_memory/resolve/resolver.rb +30 -3
  63. data/lib/claude_memory/shortcuts.rb +61 -18
  64. data/lib/claude_memory/store/prompt_journey_query.rb +87 -0
  65. data/lib/claude_memory/store/schema_manager.rb +1 -1
  66. data/lib/claude_memory/store/sqlite_store.rb +136 -0
  67. data/lib/claude_memory/sweep/maintenance.rb +31 -1
  68. data/lib/claude_memory/sweep/sweeper.rb +6 -0
  69. data/lib/claude_memory/templates/hooks.example.json +5 -0
  70. data/lib/claude_memory/version.rb +1 -1
  71. data/lib/claude_memory.rb +20 -0
  72. metadata +28 -1
@@ -0,0 +1,956 @@
1
+ > ## Documentation Index
2
+ > Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
3
+ > Use this file to discover all available pages before exploring further.
4
+
5
+ # Monitoring
6
+
7
+ > Learn how to enable and configure OpenTelemetry for Claude Code.
8
+
9
+ Track Claude Code usage, costs, and tool activity across your organization by exporting telemetry data through OpenTelemetry (OTel). Claude Code exports metrics as time series data via the standard metrics protocol, events via the logs/events protocol, and optionally distributed traces via the [traces protocol](#traces-beta). Configure your metrics, logs, and traces backends to match your monitoring requirements.
10
+
11
+ ## Quick start
12
+
13
+ Configure OpenTelemetry using environment variables:
14
+
15
+ ```bash theme={null}
16
+ # 1. Enable telemetry
17
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
18
+
19
+ # 2. Choose exporters (both are optional - configure only what you need)
20
+ export OTEL_METRICS_EXPORTER=otlp # Options: otlp, prometheus, console, none
21
+ export OTEL_LOGS_EXPORTER=otlp # Options: otlp, console, none
22
+
23
+ # 3. Configure OTLP endpoint (for OTLP exporter)
24
+ export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
25
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
26
+
27
+ # 4. Set authentication (if required)
28
+ export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"
29
+
30
+ # 5. For debugging: reduce export intervals
31
+ export OTEL_METRIC_EXPORT_INTERVAL=10000 # 10 seconds (default: 60000ms)
32
+ export OTEL_LOGS_EXPORT_INTERVAL=5000 # 5 seconds (default: 5000ms)
33
+
34
+ # 6. Run Claude Code
35
+ claude
36
+ ```
37
+
38
+ <Note>
39
+ The default export intervals are 60 seconds for metrics and 5 seconds for logs. During setup, you may want to use shorter intervals for debugging purposes. Remember to reset these for production use.
40
+ </Note>
41
+
42
+ For full configuration options, see the [OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#configuration-options).
43
+
44
+ ## Administrator configuration
45
+
46
+ Administrators can configure OpenTelemetry settings for all users through the [managed settings file](/en/settings#settings-files). This allows for centralized control of telemetry settings across an organization. See the [settings precedence](/en/settings#settings-precedence) for more information about how settings are applied.
47
+
48
+ Example managed settings configuration:
49
+
50
+ ```json theme={null}
51
+ {
52
+ "env": {
53
+ "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
54
+ "OTEL_METRICS_EXPORTER": "otlp",
55
+ "OTEL_LOGS_EXPORTER": "otlp",
56
+ "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
57
+ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4317",
58
+ "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer example-token"
59
+ }
60
+ }
61
+ ```
62
+
63
+ <Note>
64
+ Managed settings can be distributed via MDM (Mobile Device Management) or other device management solutions. Environment variables defined in the managed settings file have high precedence and cannot be overridden by users.
65
+ </Note>
66
+
67
+ ## Configuration details
68
+
69
+ ### Common configuration variables
70
+
71
+ | Environment Variable | Description | Example Values |
72
+ | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
73
+ | `CLAUDE_CODE_ENABLE_TELEMETRY` | Enables telemetry collection (required) | `1` |
74
+ | `OTEL_METRICS_EXPORTER` | Metrics exporter types, comma-separated. Use `none` to disable | `console`, `otlp`, `prometheus`, `none` |
75
+ | `OTEL_LOGS_EXPORTER` | Logs/events exporter types, comma-separated. Use `none` to disable | `console`, `otlp`, `none` |
76
+ | `OTEL_EXPORTER_OTLP_PROTOCOL` | Protocol for OTLP exporter, applies to all signals | `grpc`, `http/json`, `http/protobuf` |
77
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint for all signals | `http://localhost:4317` |
78
+ | `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` | Protocol for metrics, overrides general setting | `grpc`, `http/json`, `http/protobuf` |
79
+ | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | OTLP metrics endpoint, overrides general setting | `http://localhost:4318/v1/metrics` |
80
+ | `OTEL_EXPORTER_OTLP_LOGS_PROTOCOL` | Protocol for logs, overrides general setting | `grpc`, `http/json`, `http/protobuf` |
81
+ | `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | OTLP logs endpoint, overrides general setting | `http://localhost:4318/v1/logs` |
82
+ | `OTEL_EXPORTER_OTLP_HEADERS` | Authentication headers for OTLP | `Authorization=Bearer token` |
83
+ | `OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY` | Client key for mTLS authentication | Path to client key file |
84
+ | `OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE` | Client certificate for mTLS authentication | Path to client cert file |
85
+ | `OTEL_METRIC_EXPORT_INTERVAL` | Export interval in milliseconds (default: 60000) | `5000`, `60000` |
86
+ | `OTEL_LOGS_EXPORT_INTERVAL` | Logs export interval in milliseconds (default: 5000) | `1000`, `10000` |
87
+ | `OTEL_LOG_USER_PROMPTS` | Enable logging of user prompt content (default: disabled) | `1` to enable |
88
+ | `OTEL_LOG_TOOL_DETAILS` | Enable logging of tool parameters and input arguments in tool events and trace span attributes: Bash commands, MCP server and tool names, skill names, and tool input. Also enables custom, plugin, and MCP command names on `user_prompt` events (default: disabled) | `1` to enable |
89
+ | `OTEL_LOG_TOOL_CONTENT` | Enable logging of tool input and output content in span events (default: disabled). Requires [tracing](#traces-beta). Content is truncated at 60 KB | `1` to enable |
90
+ | `OTEL_LOG_RAW_API_BODIES` | Emit the full Anthropic Messages API request and response JSON as `api_request_body` / `api_response_body` log events (default: disabled). Bodies include the entire conversation history. Enabling this implies consent to everything `OTEL_LOG_USER_PROMPTS`, `OTEL_LOG_TOOL_DETAILS`, and `OTEL_LOG_TOOL_CONTENT` would reveal | `1` for inline bodies truncated at 60 KB, or `file:<dir>` for untruncated bodies on disk with a `body_ref` pointer in the event |
91
+ | `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | Metrics temporality preference (default: `delta`). Set to `cumulative` if your backend expects cumulative temporality | `delta`, `cumulative` |
92
+ | `CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS` | Interval for refreshing dynamic headers (default: 1740000ms / 29 minutes) | `900000` |
93
+
94
+ ### Metrics cardinality control
95
+
96
+ The following environment variables control which attributes are included in metrics to manage cardinality:
97
+
98
+ | Environment Variable | Description | Default Value | Example to Disable |
99
+ | ----------------------------------- | --------------------------------------------------------------------- | ------------- | ------------------ |
100
+ | `OTEL_METRICS_INCLUDE_SESSION_ID` | Include session.id attribute in metrics | `true` | `false` |
101
+ | `OTEL_METRICS_INCLUDE_VERSION` | Include app.version attribute in metrics | `false` | `true` |
102
+ | `OTEL_METRICS_INCLUDE_ACCOUNT_UUID` | Include user.account\_uuid and user.account\_id attributes in metrics | `true` | `false` |
103
+
104
+ These variables help control the cardinality of metrics, which affects storage requirements and query performance in your metrics backend. Lower cardinality generally means better performance and lower storage costs but less granular data for analysis.
105
+
106
+ ### Traces (beta)
107
+
108
+ Distributed tracing exports spans that link each user prompt to the API requests and tool executions it triggers, so you can view a full request as a single trace in your tracing backend.
109
+
110
+ Tracing is off by default. To enable it, set both `CLAUDE_CODE_ENABLE_TELEMETRY=1` and `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1`, then set `OTEL_TRACES_EXPORTER` to choose where spans are sent. Traces reuse the [common OTLP configuration](#common-configuration-variables) for endpoint, protocol, and headers.
111
+
112
+ | Environment Variable | Description | Example Values |
113
+ | ------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------ |
114
+ | `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA` | Enable span tracing (required). `ENABLE_ENHANCED_TELEMETRY_BETA` is also accepted | `1` |
115
+ | `OTEL_TRACES_EXPORTER` | Traces exporter types, comma-separated. Use `none` to disable | `console`, `otlp`, `none` |
116
+ | `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | Protocol for traces, overrides `OTEL_EXPORTER_OTLP_PROTOCOL` | `grpc`, `http/json`, `http/protobuf` |
117
+ | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | OTLP traces endpoint, overrides `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318/v1/traces` |
118
+ | `OTEL_TRACES_EXPORT_INTERVAL` | Span batch export interval in milliseconds (default: 5000) | `1000`, `10000` |
119
+
120
+ Spans redact user prompt text, tool input details, and tool content by default. Set `OTEL_LOG_USER_PROMPTS=1`, `OTEL_LOG_TOOL_DETAILS=1`, and `OTEL_LOG_TOOL_CONTENT=1` to include them.
121
+
122
+ When tracing is active, Bash and PowerShell subprocesses automatically inherit a `TRACEPARENT` environment variable containing the W3C trace context of the active tool execution span. This lets any subprocess that reads `TRACEPARENT` parent its own spans under the same trace, enabling end-to-end distributed tracing through scripts and commands that Claude runs.
123
+
124
+ In Agent SDK and non-interactive sessions started with `-p`, Claude Code also reads `TRACEPARENT` and `TRACESTATE` from its own environment when starting each interaction span. This lets an embedding process pass its active W3C trace context into the subprocess so Claude Code's spans appear as children of the caller's distributed trace. Interactive sessions ignore inbound `TRACEPARENT` to avoid accidentally inheriting ambient values from CI or container environments.
125
+
126
+ #### Span hierarchy
127
+
128
+ Each user prompt starts a `claude_code.interaction` root span. API calls, tool calls, and hook executions are recorded as its children. Tool spans have two child spans of their own: one for the time spent waiting on a permission decision and one for the execution itself. When the Task tool spawns a subagent, the subagent's API and tool spans nest under the parent's `claude_code.tool` span.
129
+
130
+ ```text theme={null}
131
+ claude_code.interaction
132
+ ├── claude_code.llm_request
133
+ ├── claude_code.hook (requires detailed beta tracing)
134
+ └── claude_code.tool
135
+ ├── claude_code.tool.blocked_on_user
136
+ ├── claude_code.tool.execution
137
+ └── (Task tool) subagent claude_code.llm_request / claude_code.tool spans
138
+ ```
139
+
140
+ In Agent SDK and `claude -p` sessions, `claude_code.interaction` itself becomes a child of the caller's span when `TRACEPARENT` is set in the environment.
141
+
142
+ #### Span attributes
143
+
144
+ Every span carries the [standard attributes](#standard-attributes) plus a `span.type` attribute matching its name. The tables below list the additional attributes set on each span. The `llm_request`, `tool.execution`, and `hook` spans set OpenTelemetry status `ERROR` when they record a failure; the other spans always end with status `UNSET`.
145
+
146
+ **`claude_code.interaction`**
147
+
148
+ | Attribute | Description | Gated by |
149
+ | ------------------------- | --------------------------------------------------------- | ----------------------- |
150
+ | `user_prompt` | Prompt text. Value is `<REDACTED>` unless the gate is set | `OTEL_LOG_USER_PROMPTS` |
151
+ | `user_prompt_length` | Prompt length in characters | |
152
+ | `interaction.sequence` | 1-based counter of interactions in this session | |
153
+ | `interaction.duration_ms` | Wall-clock duration of the turn | |
154
+
155
+ **`claude_code.llm_request`**
156
+
157
+ | Attribute | Description | Gated by |
158
+ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------- |
159
+ | `model` | Model identifier | |
160
+ | `gen_ai.system` | Always `anthropic`. OpenTelemetry GenAI semantic convention | |
161
+ | `gen_ai.request.model` | Same value as `model`. OpenTelemetry GenAI semantic convention | |
162
+ | `query_source` | Subsystem that issued the request, such as `repl_main_thread` or a subagent name | |
163
+ | `speed` | `fast` or `normal` | |
164
+ | `llm_request.context` | `interaction`, `tool`, or `standalone` depending on the parent span | |
165
+ | `duration_ms` | Wall-clock duration including retries | |
166
+ | `ttft_ms` | Time to first token in milliseconds | |
167
+ | `input_tokens` | Input token count from the API usage block | |
168
+ | `output_tokens` | Output token count | |
169
+ | `cache_read_tokens` | Tokens read from prompt cache | |
170
+ | `cache_creation_tokens` | Tokens written to prompt cache | |
171
+ | `request_id` | Anthropic API request ID from the `request-id` response header | |
172
+ | `gen_ai.response.id` | Same value as `request_id`. OpenTelemetry GenAI semantic convention | |
173
+ | `client_request_id` | Client-generated `x-client-request-id` of the final attempt | |
174
+ | `attempt` | Total attempts made for this request | |
175
+ | `success` | `true` or `false` | |
176
+ | `status_code` | HTTP status code when the request failed | |
177
+ | `error` | Error message when the request failed | |
178
+ | `response.has_tool_call` | `true` when the response contained tool-use blocks | |
179
+ | `stop_reason` | API response `stop_reason`, such as `end_turn`, `tool_use`, `max_tokens`, `stop_sequence`, `pause_turn`, or `refusal` | |
180
+ | `gen_ai.response.finish_reasons` | Same value as `stop_reason`, wrapped in a string array. OpenTelemetry GenAI semantic convention | |
181
+
182
+ Each retry attempt is also recorded as a `gen_ai.request.attempt` span event with `attempt` and `client_request_id` attributes.
183
+
184
+ **`claude_code.tool`**
185
+
186
+ | Attribute | Description | Gated by |
187
+ | --------------- | ----------------------------------------------------------- | ----------------------- |
188
+ | `tool_name` | Tool name | |
189
+ | `duration_ms` | Wall-clock duration including permission wait and execution | |
190
+ | `result_tokens` | Approximate token size of the tool result | |
191
+ | `file_path` | Target file path for Read, Edit, and Write tools | `OTEL_LOG_TOOL_DETAILS` |
192
+ | `full_command` | Command string for the Bash tool | `OTEL_LOG_TOOL_DETAILS` |
193
+ | `skill_name` | Skill name for the Skill tool | `OTEL_LOG_TOOL_DETAILS` |
194
+ | `subagent_type` | Subagent type for the Task tool | `OTEL_LOG_TOOL_DETAILS` |
195
+
196
+ When `OTEL_LOG_TOOL_CONTENT=1`, this span also records a `tool.output` span event whose attributes contain the tool's input and output bodies, truncated at 60 KB per attribute.
197
+
198
+ **`claude_code.tool.blocked_on_user`**
199
+
200
+ | Attribute | Description | Gated by |
201
+ | ------------- | ------------------------------------------------------------------------- | -------- |
202
+ | `duration_ms` | Time spent waiting for the permission decision | |
203
+ | `decision` | `accept` or `reject` | |
204
+ | `source` | Decision source, matching the [Tool decision event](#tool-decision-event) | |
205
+
206
+ **`claude_code.tool.execution`**
207
+
208
+ | Attribute | Description | Gated by |
209
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
210
+ | `duration_ms` | Time spent running the tool body | |
211
+ | `success` | `true` or `false` | |
212
+ | `error` | Error category string when execution failed, such as `Error:ENOENT` or `ShellError`. Contains the full error message instead when the gate is set | `OTEL_LOG_TOOL_DETAILS` |
213
+
214
+ **`claude_code.hook`**
215
+
216
+ This span is emitted only when detailed beta tracing is active, which requires `ENABLE_BETA_TRACING_DETAILED=1` and `BETA_TRACING_ENDPOINT` in addition to the trace exporter configuration above. In interactive CLI sessions, this also requires your organization to be allowlisted for the feature. Agent SDK and non-interactive `-p` sessions are not gated. It is not emitted when only `CLAUDE_CODE_ENHANCED_TELEMETRY_BETA` is set.
217
+
218
+ | Attribute | Description | Gated by |
219
+ | ------------------------ | ------------------------------------------------ | ----------------------- |
220
+ | `hook_event` | Hook event type, such as `PreToolUse` | |
221
+ | `hook_name` | Full hook name, such as `PreToolUse:Write` | |
222
+ | `num_hooks` | Number of matching hook commands executed | |
223
+ | `hook_definitions` | JSON-serialized hook configuration | `OTEL_LOG_TOOL_DETAILS` |
224
+ | `duration_ms` | Wall-clock duration of all matching hooks | |
225
+ | `num_success` | Count of hooks that completed successfully | |
226
+ | `num_blocking` | Count of hooks that returned a blocking decision | |
227
+ | `num_non_blocking_error` | Count of hooks that failed without blocking | |
228
+ | `num_cancelled` | Count of hooks cancelled before completion | |
229
+
230
+ <Note>
231
+ Additional content-bearing attributes such as `new_context`, `system_prompt_preview`, `user_system_prompt`, `tool_input`, and `response.model_output` are emitted only when detailed beta tracing is active. They are not part of the stable span schema. `user_system_prompt` additionally requires `OTEL_LOG_USER_PROMPTS=1`. It carries only the system prompt text you provide via the `systemPrompt` SDK option or `--system-prompt` and `--append-system-prompt` flags, truncated at 60 KB, and is emitted once per session rather than per request.
232
+ </Note>
233
+
234
+ ### Dynamic headers
235
+
236
+ For enterprise environments that require dynamic authentication, you can configure a script to generate headers dynamically:
237
+
238
+ #### Settings configuration
239
+
240
+ Add to your `.claude/settings.json`:
241
+
242
+ ```json theme={null}
243
+ {
244
+ "otelHeadersHelper": "/bin/generate_opentelemetry_headers.sh"
245
+ }
246
+ ```
247
+
248
+ #### Script requirements
249
+
250
+ The script must output valid JSON with string key-value pairs representing HTTP headers:
251
+
252
+ ```bash theme={null}
253
+ #!/bin/bash
254
+ # Example: Multiple headers
255
+ echo "{\"Authorization\": \"Bearer $(get-token.sh)\", \"X-API-Key\": \"$(get-api-key.sh)\"}"
256
+ ```
257
+
258
+ #### Refresh behavior
259
+
260
+ The headers helper script runs at startup and periodically thereafter to support token refresh. By default, the script runs every 29 minutes. Customize the interval with the `CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS` environment variable.
261
+
262
+ ### Multi-team organization support
263
+
264
+ Organizations with multiple teams or departments can add custom attributes to distinguish between different groups using the `OTEL_RESOURCE_ATTRIBUTES` environment variable:
265
+
266
+ ```bash theme={null}
267
+ # Add custom attributes for team identification
268
+ export OTEL_RESOURCE_ATTRIBUTES="department=engineering,team.id=platform,cost_center=eng-123"
269
+ ```
270
+
271
+ These custom attributes will be included in all metrics and events, allowing you to:
272
+
273
+ * Filter metrics by team or department
274
+ * Track costs per cost center
275
+ * Create team-specific dashboards
276
+ * Set up alerts for specific teams
277
+
278
+ <Warning>
279
+ **Important formatting requirements for OTEL\_RESOURCE\_ATTRIBUTES:**
280
+
281
+ The `OTEL_RESOURCE_ATTRIBUTES` environment variable uses comma-separated key=value pairs with strict formatting requirements:
282
+
283
+ * **No spaces allowed**: Values cannot contain spaces. For example, `user.organizationName=My Company` is invalid
284
+ * **Format**: Must be comma-separated key=value pairs: `key1=value1,key2=value2`
285
+ * **Allowed characters**: Only US-ASCII characters excluding control characters, whitespace, double quotes, commas, semicolons, and backslashes
286
+ * **Special characters**: Characters outside the allowed range must be percent-encoded
287
+
288
+ **Examples:**
289
+
290
+ ```bash theme={null}
291
+ # ❌ Invalid - contains spaces
292
+ export OTEL_RESOURCE_ATTRIBUTES="org.name=John's Organization"
293
+
294
+ # ✅ Valid - use underscores or camelCase instead
295
+ export OTEL_RESOURCE_ATTRIBUTES="org.name=Johns_Organization"
296
+ export OTEL_RESOURCE_ATTRIBUTES="org.name=JohnsOrganization"
297
+
298
+ # ✅ Valid - percent-encode special characters if needed
299
+ export OTEL_RESOURCE_ATTRIBUTES="org.name=John%27s%20Organization"
300
+ ```
301
+
302
+ Note: wrapping values in quotes doesn't escape spaces. For example, `org.name="My Company"` results in the literal value `"My Company"` (with quotes included), not `My Company`.
303
+ </Warning>
304
+
305
+ ### Example configurations
306
+
307
+ Set these environment variables before running `claude`. Each block shows a complete configuration for a different exporter or deployment scenario:
308
+
309
+ ```bash theme={null}
310
+ # Console debugging (1-second intervals)
311
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
312
+ export OTEL_METRICS_EXPORTER=console
313
+ export OTEL_METRIC_EXPORT_INTERVAL=1000
314
+
315
+ # OTLP/gRPC
316
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
317
+ export OTEL_METRICS_EXPORTER=otlp
318
+ export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
319
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
320
+
321
+ # Prometheus
322
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
323
+ export OTEL_METRICS_EXPORTER=prometheus
324
+
325
+ # Multiple exporters
326
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
327
+ export OTEL_METRICS_EXPORTER=console,otlp
328
+ export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
329
+
330
+ # Different endpoints/backends for metrics and logs
331
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
332
+ export OTEL_METRICS_EXPORTER=otlp
333
+ export OTEL_LOGS_EXPORTER=otlp
334
+ export OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf
335
+ export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://metrics.example.com:4318
336
+ export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=grpc
337
+ export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://logs.example.com:4317
338
+
339
+ # Metrics only (no events/logs)
340
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
341
+ export OTEL_METRICS_EXPORTER=otlp
342
+ export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
343
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
344
+
345
+ # Events/logs only (no metrics)
346
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
347
+ export OTEL_LOGS_EXPORTER=otlp
348
+ export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
349
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
350
+ ```
351
+
352
+ ## Available metrics and events
353
+
354
+ ### Standard attributes
355
+
356
+ All metrics and events share these standard attributes:
357
+
358
+ | Attribute | Description | Controlled By |
359
+ | ------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
360
+ | `session.id` | Unique session identifier | `OTEL_METRICS_INCLUDE_SESSION_ID` (default: true) |
361
+ | `app.version` | Current Claude Code version | `OTEL_METRICS_INCLUDE_VERSION` (default: false) |
362
+ | `organization.id` | Organization UUID (when authenticated) | Always included when available |
363
+ | `user.account_uuid` | Account UUID (when authenticated) | `OTEL_METRICS_INCLUDE_ACCOUNT_UUID` (default: true) |
364
+ | `user.account_id` | Account ID in tagged format matching Anthropic admin APIs (when authenticated), such as `user_01BWBeN28...` | `OTEL_METRICS_INCLUDE_ACCOUNT_UUID` (default: true) |
365
+ | `user.id` | Anonymous device/installation identifier, generated per Claude Code installation | Always included |
366
+ | `user.email` | User email address (when authenticated via OAuth) | Always included when available |
367
+ | `terminal.type` | Terminal type, such as `iTerm.app`, `vscode`, `cursor`, or `tmux` | Always included when detected |
368
+
369
+ Events additionally include the following attributes. These are never attached to metrics because they would cause unbounded cardinality:
370
+
371
+ * `prompt.id`: UUID correlating a user prompt with all subsequent events until the next prompt. See [Event correlation attributes](#event-correlation-attributes).
372
+ * `workspace.host_paths`: host workspace directories selected in the desktop app, as a string array
373
+
374
+ ### Metrics
375
+
376
+ Claude Code exports the following metrics:
377
+
378
+ | Metric Name | Description | Unit |
379
+ | ------------------------------------- | ----------------------------------------------- | ------ |
380
+ | `claude_code.session.count` | Count of CLI sessions started | count |
381
+ | `claude_code.lines_of_code.count` | Count of lines of code modified | count |
382
+ | `claude_code.pull_request.count` | Number of pull requests created | count |
383
+ | `claude_code.commit.count` | Number of git commits created | count |
384
+ | `claude_code.cost.usage` | Cost of the Claude Code session | USD |
385
+ | `claude_code.token.usage` | Number of tokens used | tokens |
386
+ | `claude_code.code_edit_tool.decision` | Count of code editing tool permission decisions | count |
387
+ | `claude_code.active_time.total` | Total active time in seconds | s |
388
+
389
+ ### Metric details
390
+
391
+ Each metric includes the standard attributes listed above. Metrics with additional context-specific attributes are noted below.
392
+
393
+ #### Session counter
394
+
395
+ Incremented at the start of each session.
396
+
397
+ **Attributes**:
398
+
399
+ * All [standard attributes](#standard-attributes)
400
+ * `start_type`: How the session was started. One of `"fresh"`, `"resume"`, or `"continue"`
401
+
402
+ #### Lines of code counter
403
+
404
+ Incremented when code is added or removed.
405
+
406
+ **Attributes**:
407
+
408
+ * All [standard attributes](#standard-attributes)
409
+ * `type`: (`"added"`, `"removed"`)
410
+
411
+ #### Pull request counter
412
+
413
+ Incremented when creating pull requests via Claude Code.
414
+
415
+ **Attributes**:
416
+
417
+ * All [standard attributes](#standard-attributes)
418
+
419
+ #### Commit counter
420
+
421
+ Incremented when creating git commits via Claude Code.
422
+
423
+ **Attributes**:
424
+
425
+ * All [standard attributes](#standard-attributes)
426
+
427
+ #### Cost counter
428
+
429
+ Incremented after each API request.
430
+
431
+ **Attributes**:
432
+
433
+ * All [standard attributes](#standard-attributes)
434
+ * `model`: Model identifier (for example, "claude-sonnet-4-6")
435
+ * `query_source`: Category of the subsystem that issued the request. One of `"main"`, `"subagent"`, or `"auxiliary"`
436
+ * `speed`: `"fast"` when the request used fast mode. Absent otherwise
437
+ * `effort`: [Effort level](/en/model-config#adjust-effort-level) applied to the request: `"low"`, `"medium"`, `"high"`, `"xhigh"`, or `"max"`. Absent when the model does not support effort.
438
+
439
+ #### Token counter
440
+
441
+ Incremented after each API request.
442
+
443
+ **Attributes**:
444
+
445
+ * All [standard attributes](#standard-attributes)
446
+ * `type`: (`"input"`, `"output"`, `"cacheRead"`, `"cacheCreation"`)
447
+ * `model`: Model identifier (for example, "claude-sonnet-4-6")
448
+ * `query_source`: Category of the subsystem that issued the request. One of `"main"`, `"subagent"`, or `"auxiliary"`
449
+ * `speed`: `"fast"` when the request used fast mode. Absent otherwise
450
+ * `effort`: [Effort level](/en/model-config#adjust-effort-level) applied to the request. See [Cost counter](#cost-counter) for details.
451
+
452
+ #### Code edit tool decision counter
453
+
454
+ Incremented when user accepts or rejects Edit, Write, or NotebookEdit tool usage.
455
+
456
+ **Attributes**:
457
+
458
+ * All [standard attributes](#standard-attributes)
459
+ * `tool_name`: Tool name (`"Edit"`, `"Write"`, `"NotebookEdit"`)
460
+ * `decision`: User decision (`"accept"`, `"reject"`)
461
+ * `source`: Where the decision came from. One of `"config"`, `"hook"`, `"user_permanent"`, `"user_temporary"`, `"user_abort"`, or `"user_reject"`. See the [Tool decision event](#tool-decision-event) for what each value means.
462
+ * `language`: Programming language of the edited file, such as `"TypeScript"`, `"Python"`, `"JavaScript"`, or `"Markdown"`. Returns `"unknown"` for unrecognized file extensions.
463
+
464
+ #### Active time counter
465
+
466
+ Tracks actual time spent actively using Claude Code, excluding idle time. This metric is incremented during user interactions (typing, reading responses) and during CLI processing (tool execution, AI response generation).
467
+
468
+ **Attributes**:
469
+
470
+ * All [standard attributes](#standard-attributes)
471
+ * `type`: `"user"` for keyboard interactions, `"cli"` for tool execution and AI responses
472
+
473
+ ### Events
474
+
475
+ Claude Code exports the following events via OpenTelemetry logs/events (when `OTEL_LOGS_EXPORTER` is configured):
476
+
477
+ #### Event correlation attributes
478
+
479
+ When a user submits a prompt, Claude Code may make multiple API calls and run several tools. The `prompt.id` attribute lets you tie all of those events back to the single prompt that triggered them.
480
+
481
+ | Attribute | Description |
482
+ | ----------- | ------------------------------------------------------------------------------------ |
483
+ | `prompt.id` | UUID v4 identifier linking all events produced while processing a single user prompt |
484
+
485
+ To trace all activity triggered by a single prompt, filter your events by a specific `prompt.id` value. This returns the user\_prompt event, any api\_request events, and any tool\_result events that occurred while processing that prompt.
486
+
487
+ <Note>
488
+ `prompt.id` is intentionally excluded from metrics because each prompt generates a unique ID, which would create an ever-growing number of time series. Use it for event-level analysis and audit trails only.
489
+ </Note>
490
+
491
+ #### User prompt event
492
+
493
+ Logged when a user submits a prompt.
494
+
495
+ **Event Name**: `claude_code.user_prompt`
496
+
497
+ **Attributes**:
498
+
499
+ * All [standard attributes](#standard-attributes)
500
+ * `event.name`: `"user_prompt"`
501
+ * `event.timestamp`: ISO 8601 timestamp
502
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
503
+ * `prompt_length`: Length of the prompt
504
+ * `prompt`: Prompt content (redacted by default, enable with `OTEL_LOG_USER_PROMPTS=1`)
505
+ * `command_name`: Command name when the prompt invokes one. Built-in and bundled command names such as `compact` or `debug` are emitted as-is; aliases such as `reset` emit as typed rather than the canonical name. Custom, plugin, and MCP command names collapse to `custom` or `mcp` unless `OTEL_LOG_TOOL_DETAILS=1` is set
506
+ * `command_source`: Origin of the command when present: `builtin`, `custom`, or `mcp`. Plugin-provided commands report as `custom`
507
+
508
+ #### Tool result event
509
+
510
+ Logged when a tool completes execution.
511
+
512
+ **Event Name**: `claude_code.tool_result`
513
+
514
+ **Attributes**:
515
+
516
+ * All [standard attributes](#standard-attributes)
517
+ * `event.name`: `"tool_result"`
518
+ * `event.timestamp`: ISO 8601 timestamp
519
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
520
+ * `tool_name`: Name of the tool
521
+ * `tool_use_id`: Unique identifier for this tool invocation. Matches the `tool_use_id` passed to hooks, allowing correlation between OTel events and hook-captured data.
522
+ * `success`: `"true"` or `"false"`
523
+ * `duration_ms`: Execution time in milliseconds
524
+ * `error_type`: Error category string when the tool failed, such as `"Error:ENOENT"` or `"ShellError"`
525
+ * `error` (when `OTEL_LOG_TOOL_DETAILS=1`): Full error message when the tool failed
526
+ * `decision_type`: Either `"accept"` or `"reject"`
527
+ * `decision_source`: Where the decision came from. One of `"config"`, `"hook"`, `"user_permanent"`, `"user_temporary"`, `"user_abort"`, or `"user_reject"`. See the [Tool decision event](#tool-decision-event) for what each value means.
528
+ * `tool_input_size_bytes`: Size of the JSON-serialized tool input in bytes
529
+ * `tool_result_size_bytes`: Size of the tool result in bytes
530
+ * `mcp_server_scope`: MCP server scope identifier (for MCP tools)
531
+ * `tool_parameters` (when `OTEL_LOG_TOOL_DETAILS=1`): JSON string containing tool-specific parameters:
532
+ * For Bash tool: includes `bash_command`, `full_command`, `timeout`, `description`, `dangerouslyDisableSandbox`, and `git_commit_id` (the commit SHA, when a `git commit` command succeeds)
533
+ * For MCP tools: includes `mcp_server_name`, `mcp_tool_name`
534
+ * For Skill tool: includes `skill_name`
535
+ * For Task tool: includes `subagent_type`
536
+ * `tool_input` (when `OTEL_LOG_TOOL_DETAILS=1`): JSON-serialized tool arguments. Individual values over 512 characters are truncated, and the full payload is bounded to \~4 K characters. Applies to all tools including MCP tools.
537
+
538
+ #### API request event
539
+
540
+ Logged for each API request to Claude.
541
+
542
+ **Event Name**: `claude_code.api_request`
543
+
544
+ **Attributes**:
545
+
546
+ * All [standard attributes](#standard-attributes)
547
+ * `event.name`: `"api_request"`
548
+ * `event.timestamp`: ISO 8601 timestamp
549
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
550
+ * `model`: Model used (for example, "claude-sonnet-4-6")
551
+ * `cost_usd`: Estimated cost in USD
552
+ * `duration_ms`: Request duration in milliseconds
553
+ * `input_tokens`: Number of input tokens
554
+ * `output_tokens`: Number of output tokens
555
+ * `cache_read_tokens`: Number of tokens read from cache
556
+ * `cache_creation_tokens`: Number of tokens used for cache creation
557
+ * `request_id`: Anthropic API request ID from the response's `request-id` header, such as `"req_011..."`. Present only when the API returns one.
558
+ * `speed`: `"fast"` or `"normal"`, indicating whether fast mode was active
559
+ * `query_source`: Subsystem that issued the request, such as `"repl_main_thread"`, `"compact"`, or a subagent name
560
+ * `effort`: [Effort level](/en/model-config#adjust-effort-level) applied to the request: `"low"`, `"medium"`, `"high"`, `"xhigh"`, or `"max"`. Absent when the model does not support effort.
561
+
562
+ #### API error event
563
+
564
+ Logged when an API request to Claude fails.
565
+
566
+ **Event Name**: `claude_code.api_error`
567
+
568
+ **Attributes**:
569
+
570
+ * All [standard attributes](#standard-attributes)
571
+ * `event.name`: `"api_error"`
572
+ * `event.timestamp`: ISO 8601 timestamp
573
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
574
+ * `model`: Model used (for example, "claude-sonnet-4-6")
575
+ * `error`: Error message
576
+ * `status_code`: HTTP status code as a number. Absent for non-HTTP errors such as connection failures.
577
+ * `duration_ms`: Request duration in milliseconds
578
+ * `attempt`: Total number of attempts made, including the initial request (`1` means no retries occurred)
579
+ * `request_id`: Anthropic API request ID from the response's `request-id` header, such as `"req_011..."`. Present only when the API returns one.
580
+ * `speed`: `"fast"` or `"normal"`, indicating whether fast mode was active
581
+ * `query_source`: Subsystem that issued the request, such as `"repl_main_thread"`, `"compact"`, or a subagent name
582
+ * `effort`: [Effort level](/en/model-config#adjust-effort-level) applied to the request. Absent when the model does not support effort.
583
+
584
+ #### API request body event
585
+
586
+ Logged for each API request attempt when `OTEL_LOG_RAW_API_BODIES` is set. One event is emitted per attempt, so retries with adjusted parameters each produce their own event.
587
+
588
+ **Event Name**: `claude_code.api_request_body`
589
+
590
+ **Attributes**:
591
+
592
+ * All [standard attributes](#standard-attributes)
593
+ * `event.name`: `"api_request_body"`
594
+ * `event.timestamp`: ISO 8601 timestamp
595
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
596
+ * `body`: JSON-serialized Messages API request parameters (system prompt, messages, tools, etc.), truncated at 60 KB. Extended-thinking content in prior assistant turns is redacted. Emitted only in inline mode (`OTEL_LOG_RAW_API_BODIES=1`).
597
+ * `body_ref`: Absolute path to a `<dir>/<uuid>.request.json` file containing the untruncated body. Emitted only in file mode (`OTEL_LOG_RAW_API_BODIES=file:<dir>`).
598
+ * `body_length`: Untruncated body length. UTF-8 bytes when `OTEL_LOG_RAW_API_BODIES=file:<dir>`, or UTF-16 code units when `=1`
599
+ * `body_truncated`: `"true"` when inline truncation occurred. Absent in file mode and when no truncation occurred.
600
+ * `model`: Model identifier from the request parameters
601
+ * `query_source`: Subsystem that issued the request (for example, `"compact"`)
602
+
603
+ #### API response body event
604
+
605
+ Logged for each successful API response when `OTEL_LOG_RAW_API_BODIES` is set.
606
+
607
+ **Event Name**: `claude_code.api_response_body`
608
+
609
+ **Attributes**:
610
+
611
+ * All [standard attributes](#standard-attributes)
612
+ * `event.name`: `"api_response_body"`
613
+ * `event.timestamp`: ISO 8601 timestamp
614
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
615
+ * `body`: JSON-serialized Messages API response (id, content blocks, usage, stop reason), truncated at 60 KB. Extended-thinking content is redacted. Emitted only in inline mode (`OTEL_LOG_RAW_API_BODIES=1`).
616
+ * `body_ref`: Absolute path to a `<dir>/<request_id>.response.json` file containing the untruncated body. Emitted only in file mode (`OTEL_LOG_RAW_API_BODIES=file:<dir>`).
617
+ * `body_length`: Untruncated body length. UTF-8 bytes when `OTEL_LOG_RAW_API_BODIES=file:<dir>`, or UTF-16 code units when `=1`
618
+ * `body_truncated`: `"true"` when inline truncation occurred. Absent in file mode and when no truncation occurred.
619
+ * `model`: Model identifier
620
+ * `query_source`: Subsystem that issued the request
621
+ * `request_id`: Anthropic API request ID from the response's `request-id` header, such as `"req_011..."`. Present only when the API returns one.
622
+
623
+ #### Tool decision event
624
+
625
+ Logged when a tool permission decision is made (accept/reject).
626
+
627
+ **Event Name**: `claude_code.tool_decision`
628
+
629
+ **Attributes**:
630
+
631
+ * All [standard attributes](#standard-attributes)
632
+ * `event.name`: `"tool_decision"`
633
+ * `event.timestamp`: ISO 8601 timestamp
634
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
635
+ * `tool_name`: Name of the tool (for example, "Read", "Edit", "Write", "NotebookEdit")
636
+ * `tool_use_id`: Unique identifier for this tool invocation. Matches the `tool_use_id` passed to hooks, allowing correlation between OTel events and hook-captured data.
637
+ * `decision`: Either `"accept"` or `"reject"`
638
+ * `source`: Where the decision came from:
639
+ * `"config"`: Decided automatically without prompting, based on project settings, enterprise managed policy, `--allowedTools` or `--disallowedTools` flags, the active permission mode, or because the tool is inherently safe.
640
+ * `"hook"`: A `PreToolUse` or `PermissionRequest` hook returned the decision.
641
+ * `"user_permanent"`: Emitted when the user chose "Always allow" when prompted, saving a rule to their personal settings. Also emitted for later calls that match that saved rule. Treated as an accept.
642
+ * `"user_temporary"`: Emitted when the user chose "Yes" or "Yes, for this session" when prompted, without saving a rule. Also emitted for later calls in the same session that match that session-scoped allow. Treated as an accept.
643
+ * `"user_abort"`: Emitted when the user dismissed the permission prompt without answering. Treated as a reject.
644
+ * `"user_reject"`: Emitted when the user chose "No" when prompted, or a call matched a deny rule in their personal settings. Treated as a reject.
645
+
646
+ #### Permission mode changed event
647
+
648
+ Logged when the permission mode changes, for example from `Shift+Tab` cycling, exiting plan mode, or an auto mode gate check.
649
+
650
+ **Event Name**: `claude_code.permission_mode_changed`
651
+
652
+ **Attributes**:
653
+
654
+ * All [standard attributes](#standard-attributes)
655
+ * `event.name`: `"permission_mode_changed"`
656
+ * `event.timestamp`: ISO 8601 timestamp
657
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
658
+ * `from_mode`: The previous permission mode, for example `"default"`, `"plan"`, `"acceptEdits"`, `"auto"`, or `"bypassPermissions"`
659
+ * `to_mode`: The new permission mode
660
+ * `trigger`: What caused the change. One of `"shift_tab"`, `"exit_plan_mode"`, `"auto_gate_denied"`, or `"auto_opt_in"`. Absent when the transition originates from the SDK or bridge
661
+
662
+ #### Auth event
663
+
664
+ Logged when `/login` or `/logout` completes.
665
+
666
+ **Event Name**: `claude_code.auth`
667
+
668
+ **Attributes**:
669
+
670
+ * All [standard attributes](#standard-attributes)
671
+ * `event.name`: `"auth"`
672
+ * `event.timestamp`: ISO 8601 timestamp
673
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
674
+ * `action`: `"login"` or `"logout"`
675
+ * `success`: `"true"` or `"false"`
676
+ * `auth_method`: Authentication method, such as `"oauth"`
677
+ * `error_category`: Categorical error kind when the action failed. The raw error message is never included
678
+ * `status_code`: HTTP status code as a string when the action failed with an HTTP error
679
+
680
+ #### MCP server connection event
681
+
682
+ Logged when an MCP server connects, disconnects, or fails to connect.
683
+
684
+ **Event Name**: `claude_code.mcp_server_connection`
685
+
686
+ **Attributes**:
687
+
688
+ * All [standard attributes](#standard-attributes)
689
+ * `event.name`: `"mcp_server_connection"`
690
+ * `event.timestamp`: ISO 8601 timestamp
691
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
692
+ * `status`: `"connected"`, `"failed"`, or `"disconnected"`
693
+ * `transport_type`: Server transport, such as `"stdio"`, `"sse"`, or `"http"`
694
+ * `server_scope`: Scope the server is configured at, such as `"user"`, `"project"`, or `"local"`
695
+ * `duration_ms`: Connection attempt duration in milliseconds
696
+ * `error_code`: Error code when the connection failed
697
+ * `server_name` (when `OTEL_LOG_TOOL_DETAILS=1`): Configured server name
698
+ * `error` (when `OTEL_LOG_TOOL_DETAILS=1`): Full error message when the connection failed
699
+
700
+ #### Internal error event
701
+
702
+ Logged when Claude Code catches an unexpected internal error. Only the error class name and an errno-style code are recorded. The error message and stack trace are never included. This event is not emitted when running against Bedrock, Vertex, or Foundry, or when `DISABLE_ERROR_REPORTING` is set.
703
+
704
+ **Event Name**: `claude_code.internal_error`
705
+
706
+ **Attributes**:
707
+
708
+ * All [standard attributes](#standard-attributes)
709
+ * `event.name`: `"internal_error"`
710
+ * `event.timestamp`: ISO 8601 timestamp
711
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
712
+ * `error_name`: Error class name, such as `"TypeError"` or `"SyntaxError"`
713
+ * `error_code`: Node.js errno code such as `"ENOENT"` when present on the error
714
+
715
+ #### Plugin installed event
716
+
717
+ Logged when a plugin finishes installing, from both the `claude plugin install` CLI command and the interactive `/plugin` UI.
718
+
719
+ **Event Name**: `claude_code.plugin_installed`
720
+
721
+ **Attributes**:
722
+
723
+ * All [standard attributes](#standard-attributes)
724
+ * `event.name`: `"plugin_installed"`
725
+ * `event.timestamp`: ISO 8601 timestamp
726
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
727
+ * `marketplace.is_official`: `"true"` if the marketplace is an official Anthropic marketplace, `"false"` otherwise
728
+ * `install.trigger`: `"cli"` or `"ui"`
729
+ * `plugin.name`: Name of the installed plugin. For third-party marketplaces this is included only when `OTEL_LOG_TOOL_DETAILS=1`
730
+ * `plugin.version`: Plugin version when declared in the marketplace entry. For third-party marketplaces this is included only when `OTEL_LOG_TOOL_DETAILS=1`
731
+ * `marketplace.name`: Marketplace the plugin was installed from. For third-party marketplaces this is included only when `OTEL_LOG_TOOL_DETAILS=1`
732
+
733
+ #### Skill activated event
734
+
735
+ Logged when a skill is invoked, whether Claude calls it through the Skill tool or you run it as a `/` command.
736
+
737
+ **Event Name**: `claude_code.skill_activated`
738
+
739
+ **Attributes**:
740
+
741
+ * All [standard attributes](#standard-attributes)
742
+ * `event.name`: `"skill_activated"`
743
+ * `event.timestamp`: ISO 8601 timestamp
744
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
745
+ * `skill.name`: Name of the skill. For user-defined and third-party plugin skills the value is the placeholder `"custom_skill"` unless `OTEL_LOG_TOOL_DETAILS=1`
746
+ * `invocation_trigger`: How the skill was triggered (`"user-slash"`, `"claude-proactive"`, or `"nested-skill"`)
747
+ * `skill.source`: Where the skill was loaded from (for example, `"bundled"`, `"userSettings"`, `"projectSettings"`, `"plugin"`)
748
+ * `plugin.name` (when `OTEL_LOG_TOOL_DETAILS=1` or the plugin is from an official marketplace): Name of the owning plugin when the skill is provided by a plugin
749
+ * `marketplace.name` (when `OTEL_LOG_TOOL_DETAILS=1` or the plugin is from an official marketplace): Marketplace the owning plugin was installed from, when the skill is provided by a plugin
750
+
751
+ #### At mention event
752
+
753
+ Logged when Claude Code resolves an `@`-mention in a prompt. Not every mention emits an event: early-exit paths such as permission denials, oversized files, PDF reference attachments, and directory listing failures return without logging.
754
+
755
+ **Event Name**: `claude_code.at_mention`
756
+
757
+ **Attributes**:
758
+
759
+ * All [standard attributes](#standard-attributes)
760
+ * `event.name`: `"at_mention"`
761
+ * `event.timestamp`: ISO 8601 timestamp
762
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
763
+ * `mention_type`: Type of mention (`"file"`, `"directory"`, `"agent"`, `"mcp_resource"`)
764
+ * `success`: Whether the mention resolved successfully (`"true"` or `"false"`)
765
+
766
+ #### API retries exhausted event
767
+
768
+ Logged once when an API request fails after more than one attempt. Emitted alongside the final `api_error` event.
769
+
770
+ **Event Name**: `claude_code.api_retries_exhausted`
771
+
772
+ **Attributes**:
773
+
774
+ * All [standard attributes](#standard-attributes)
775
+ * `event.name`: `"api_retries_exhausted"`
776
+ * `event.timestamp`: ISO 8601 timestamp
777
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
778
+ * `model`: Model used
779
+ * `error`: Final error message
780
+ * `status_code`: HTTP status code as a number. Absent for non-HTTP errors.
781
+ * `total_attempts`: Total number of attempts made
782
+ * `total_retry_duration_ms`: Total wall-clock time across all attempts
783
+ * `speed`: `"fast"` or `"normal"`
784
+
785
+ #### Hook execution start event
786
+
787
+ Logged when one or more hooks begin executing for a hook event.
788
+
789
+ **Event Name**: `claude_code.hook_execution_start`
790
+
791
+ **Attributes**:
792
+
793
+ * All [standard attributes](#standard-attributes)
794
+ * `event.name`: `"hook_execution_start"`
795
+ * `event.timestamp`: ISO 8601 timestamp
796
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
797
+ * `hook_event`: Hook event type, such as `"PreToolUse"` or `"PostToolUse"`
798
+ * `hook_name`: Full hook name including matcher, such as `"PreToolUse:Write"`
799
+ * `num_hooks`: Number of matching hook commands
800
+ * `managed_only`: `"true"` when only managed-policy hooks are permitted
801
+ * `hook_source`: `"policySettings"` or `"merged"`
802
+ * `hook_definitions`: JSON-serialized hook configuration. Included only when both detailed beta tracing and `OTEL_LOG_TOOL_DETAILS=1` are enabled
803
+
804
+ #### Hook execution complete event
805
+
806
+ Logged when all hooks for a hook event have finished.
807
+
808
+ **Event Name**: `claude_code.hook_execution_complete`
809
+
810
+ **Attributes**:
811
+
812
+ * All [standard attributes](#standard-attributes)
813
+ * `event.name`: `"hook_execution_complete"`
814
+ * `event.timestamp`: ISO 8601 timestamp
815
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
816
+ * `hook_event`: Hook event type
817
+ * `hook_name`: Full hook name including matcher
818
+ * `num_hooks`: Number of matching hook commands
819
+ * `num_success`: Count that completed successfully
820
+ * `num_blocking`: Count that returned a blocking decision
821
+ * `num_non_blocking_error`: Count that failed without blocking
822
+ * `num_cancelled`: Count cancelled before completion
823
+ * `total_duration_ms`: Wall-clock duration of all matching hooks
824
+ * `managed_only`: `"true"` when only managed-policy hooks are permitted
825
+ * `hook_source`: `"policySettings"` or `"merged"`
826
+ * `hook_definitions`: JSON-serialized hook configuration. Included only when both detailed beta tracing and `OTEL_LOG_TOOL_DETAILS=1` are enabled
827
+
828
+ #### Compaction event
829
+
830
+ Logged when conversation compaction completes.
831
+
832
+ **Event Name**: `claude_code.compaction`
833
+
834
+ **Attributes**:
835
+
836
+ * All [standard attributes](#standard-attributes)
837
+ * `event.name`: `"compaction"`
838
+ * `event.timestamp`: ISO 8601 timestamp
839
+ * `event.sequence`: monotonically increasing counter for ordering events within a session
840
+ * `trigger`: `"auto"` or `"manual"`
841
+ * `success`: `"true"` or `"false"`
842
+ * `duration_ms`: Compaction duration
843
+ * `pre_tokens`: Approximate token count before compaction
844
+ * `post_tokens`: Approximate token count after compaction
845
+ * `error`: Error message when compaction failed
846
+
847
+ ## Interpret metrics and events data
848
+
849
+ The exported metrics and events support a range of analyses:
850
+
851
+ ### Usage monitoring
852
+
853
+ | Metric | Analysis Opportunity |
854
+ | ------------------------------------------------------------- | --------------------------------------------------------- |
855
+ | `claude_code.token.usage` | Break down by `type` (input/output), user, team, or model |
856
+ | `claude_code.session.count` | Track adoption and engagement over time |
857
+ | `claude_code.lines_of_code.count` | Measure productivity by tracking code additions/removals |
858
+ | `claude_code.commit.count` & `claude_code.pull_request.count` | Understand impact on development workflows |
859
+
860
+ ### Cost monitoring
861
+
862
+ The `claude_code.cost.usage` metric helps with:
863
+
864
+ * Tracking usage trends across teams or individuals
865
+ * Identifying high-usage sessions for optimization
866
+
867
+ <Note>
868
+ Cost metrics are approximations. For official billing data, refer to your API provider (Claude Console, Amazon Bedrock, or Google Cloud Vertex).
869
+ </Note>
870
+
871
+ ### Alerting and segmentation
872
+
873
+ Common alerts to consider:
874
+
875
+ * Cost spikes
876
+ * Unusual token consumption
877
+ * High session volume from specific users
878
+
879
+ All metrics can be segmented by `user.account_uuid`, `user.account_id`, `organization.id`, `session.id`, `model`, and `app.version`.
880
+
881
+ ### Detect retry exhaustion
882
+
883
+ Claude Code retries failed API requests internally and emits a single `claude_code.api_error` event only after it gives up, so the event itself is the terminal signal for that request. Intermediate retry attempts are not logged as separate events.
884
+
885
+ The `attempt` attribute on the event records how many attempts were made in total. A value greater than `CLAUDE_CODE_MAX_RETRIES` (default `10`) indicates the request exhausted all retries on a transient error. A lower value indicates a non-retryable error such as a `400` response.
886
+
887
+ To distinguish a session that recovered from one that stalled, group events by `session.id` and check whether a later `api_request` event exists after the error.
888
+
889
+ ### Event analysis
890
+
891
+ The event data provides detailed insights into Claude Code interactions:
892
+
893
+ **Tool Usage Patterns**: analyze tool result events to identify:
894
+
895
+ * Most frequently used tools
896
+ * Tool success rates
897
+ * Average tool execution times
898
+ * Error patterns by tool type
899
+
900
+ **Performance Monitoring**: track API request durations and tool execution times to identify performance bottlenecks.
901
+
902
+ ## Backend considerations
903
+
904
+ Your choice of metrics, logs, and traces backends determines the types of analyses you can perform:
905
+
906
+ ### For metrics
907
+
908
+ * **Time series databases (for example, Prometheus)**: Rate calculations, aggregated metrics
909
+ * **Columnar stores (for example, ClickHouse)**: Complex queries, unique user analysis
910
+ * **Full-featured observability platforms (for example, Honeycomb, Datadog)**: Advanced querying, visualization, alerting
911
+
912
+ ### For events/logs
913
+
914
+ * **Log aggregation systems (for example, Elasticsearch, Loki)**: Full-text search, log analysis
915
+ * **Columnar stores (for example, ClickHouse)**: Structured event analysis
916
+ * **Full-featured observability platforms (for example, Honeycomb, Datadog)**: Correlation between metrics and events
917
+
918
+ ### For traces
919
+
920
+ Choose a backend that supports distributed trace storage and span correlation:
921
+
922
+ * **Distributed tracing systems (for example, Jaeger, Zipkin, Grafana Tempo)**: Span visualization, request waterfalls, latency analysis
923
+ * **Full-featured observability platforms (for example, Honeycomb, Datadog)**: Trace search and correlation with metrics and logs
924
+
925
+ For organizations requiring Daily/Weekly/Monthly Active User (DAU/WAU/MAU) metrics, consider backends that support efficient unique value queries.
926
+
927
+ ## Service information
928
+
929
+ All metrics and events are exported with the following resource attributes:
930
+
931
+ * `service.name`: `claude-code`
932
+ * `service.version`: Current Claude Code version
933
+ * `os.type`: Operating system type (for example, `linux`, `darwin`, `windows`)
934
+ * `os.version`: Operating system version string
935
+ * `host.arch`: Host architecture (for example, `amd64`, `arm64`)
936
+ * `wsl.version`: WSL version number (only present when running on Windows Subsystem for Linux)
937
+ * Meter Name: `com.anthropic.claude_code`
938
+
939
+ ## ROI measurement resources
940
+
941
+ For a comprehensive guide on measuring return on investment for Claude Code, including telemetry setup, cost analysis, productivity metrics, and automated reporting, see the [Claude Code ROI Measurement Guide](https://github.com/anthropics/claude-code-monitoring-guide). This repository provides ready-to-use Docker Compose configurations, Prometheus and OpenTelemetry setups, and templates for generating productivity reports integrated with tools like Linear.
942
+
943
+ ## Security and privacy
944
+
945
+ * OpenTelemetry export to your backend is opt-in and requires explicit configuration. For Anthropic's separate operational telemetry and how to disable it, see [Data usage](/en/data-usage#telemetry-services)
946
+ * Raw file contents and code snippets are not included in metrics or events. Trace spans are a separate data path: see the `OTEL_LOG_TOOL_CONTENT` bullet below
947
+ * When authenticated via OAuth, `user.email` is included in telemetry attributes. If this is a concern for your organization, work with your telemetry backend to filter or redact this field
948
+ * User prompt content is not collected by default. Only prompt length is recorded. To include prompt content, set `OTEL_LOG_USER_PROMPTS=1`
949
+ * Tool input arguments and parameters are not logged by default. To include them, set `OTEL_LOG_TOOL_DETAILS=1`. When enabled, `tool_result` events include a `tool_parameters` attribute with Bash commands, MCP server and tool names, and skill names, plus a `tool_input` attribute with file paths, URLs, search patterns, and other arguments. `user_prompt` events include the verbatim `command_name` for custom, plugin, and MCP commands. Trace spans include the same `tool_input` attribute and input-derived attributes such as `file_path`. Individual values over 512 characters are truncated and the total is bounded to \~4 K characters, but the arguments may still contain sensitive values. Configure your telemetry backend to filter or redact these attributes as needed
950
+ * Tool input and output content is not logged in trace spans by default. To include it, set `OTEL_LOG_TOOL_CONTENT=1`. When enabled, span events include full tool input and output content truncated at 60 KB per span. This can include raw file contents from Read tool results and Bash command output. Configure your telemetry backend to filter or redact these attributes as needed
951
+ * Raw Anthropic Messages API request and response bodies are not logged by default. To include them, set `OTEL_LOG_RAW_API_BODIES`. With `=1`, each API call emits `api_request_body` and `api_response_body` log events whose `body` attribute is the JSON-serialized payload, truncated at 60 KB. With `=file:<dir>`, untruncated bodies are written to `.request.json` and `.response.json` files under that directory and the events carry a `body_ref` path instead of the inline body. Ship the directory with a log collector or sidecar rather than through the telemetry stream. In both modes, bodies contain the full conversation history (system prompt, every prior user and assistant turn, tool results), so enabling this implies consent to everything the other `OTEL_LOG_*` content flags would reveal. Claude's extended-thinking content is always redacted from these bodies regardless of other settings
952
+
953
+ ## Monitor Claude Code on Amazon Bedrock
954
+
955
+ For detailed Claude Code usage monitoring guidance for Amazon Bedrock, see [Claude Code Monitoring Implementation (Bedrock)](https://github.com/aws-solutions-library-samples/guidance-for-claude-code-with-amazon-bedrock/blob/main/assets/docs/MONITORING.md).
956
+