Skip to content
Last9
Book demo

Claude Code

Send Claude Code session telemetry — prompts, tool calls, API costs, and errors — to Last9 via OpenTelemetry.

Claude Code emits structured telemetry for every developer session: prompts submitted, tools invoked, API calls made, and costs incurred. Routing this data to Last9 lets you analyze AI usage patterns, track per-user spend, audit tool decisions, and alert on error rates — all within your existing observability stack.

Claude Code exports three OpenTelemetry signal types:

  • Logs — individual events per prompt, API call, tool execution, and error
  • Metrics — aggregated counters for cost, tokens, sessions, and code edits
  • Traces (beta) — span-based correlation linking a prompt to all its API calls and tool executions

What gets exported

Logs (events)

Claude Code exports five event types as OpenTelemetry log records. All share a prompt.id UUID that lets you reconstruct the full sequence for a single user interaction.

EventEmitted whenKey attributes
claude_code.user_promptUser submits a promptprompt.length, session.id, user.email
claude_code.api_requestClaude API respondsllm.usage.total_tokens, cost_usd, model, duration_ms
claude_code.api_errorAPI call failserror.message, http.status_code, retry_attempt
claude_code.tool_resultTool execution completestool.name, tool.success, duration_ms, bash.command
claude_code.tool_decisionPermission prompt resolvedtool.name, tool.decision (accept/reject), decision.source

Metrics

Claude Code exports eight counters as OpenTelemetry metrics. These are aggregated — no per-prompt IDs — making them low-cardinality and suitable for dashboards and alerts.

MetricUnitKey attributes
claude_code.session.countcountsession.id, user.email, app.version
claude_code.cost.usageUSDmodel, user.email
claude_code.token.usagetokenstype (input/output/cacheRead/cacheCreation), model
claude_code.lines_of_code.countcounttype (added/removed)
claude_code.pull_request.countcountstandard attributes
claude_code.commit.countcountstandard attributes
claude_code.code_edit_tool.decisioncounttool_name, decision (accept/reject), source, language
claude_code.active_time.totalsecondstype (user/cli)

Prerequisites

  1. Last9 account — Sign up at app.last9.io
  2. Claude Code — Installed and authenticated (claude --version should work)
  3. OTLP credentials — Get your endpoint and auth header from Integrations → OpenTelemetry

Setup

  1. Get your Last9 OTLP credentials

    Navigate to Integrations → OpenTelemetry in your Last9 dashboard. Copy:

    • OTLP Endpoint (e.g., https://otlp-aps1.last9.io:443)
    • Authorization header (e.g., Basic <base64-token>)
  2. Set environment variables

    Add the following to your shell profile (~/.zshrc, ~/.bashrc, or equivalent):

    # Required: enable Claude Code telemetry
    export CLAUDE_CODE_ENABLE_TELEMETRY=1
    # Export both logs and metrics to Last9
    export OTEL_LOGS_EXPORTER=otlp
    export OTEL_METRICS_EXPORTER=otlp
    # Last9 OTLP destination
    export OTEL_EXPORTER_OTLP_ENDPOINT="https://<your-last9-otlp-endpoint>"
    export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <your-last9-auth-token>"
    export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
    # Required for metrics: Last9 expects cumulative counters
    export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative
    # Required for traces: currently in beta
    export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1
    # Identify your sessions
    export OTEL_SERVICE_NAME="claude-code"
    export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=local,team=<your-team>"

    Then reload your shell:

    source ~/.zshrc
  3. Start a Claude Code session

    claude "summarize what this repo does"
    • Logs flush within 5 seconds of each event
    • Metrics flush every 60 seconds by default
  4. Verify data is arriving

    • Logs — navigate to Logs in Last9, filter by service.name = claude-code
    • Metrics — navigate to Metrics, search for claude_code_cost_usage_total

Configuration reference

Core

VariableDefaultDescription
CLAUDE_CODE_ENABLE_TELEMETRY0Set to 1 to enable all telemetry
OTEL_LOGS_EXPORTERnoneotlp to export logs to Last9
OTEL_METRICS_EXPORTERnoneotlp to export metrics to Last9
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA0Set to 1 to enable trace export (beta)
OTEL_EXPORTER_OTLP_ENDPOINTLast9 OTLP endpoint URL
OTEL_EXPORTER_OTLP_HEADERSAuthorization=Basic <token>
OTEL_SERVICE_NAMEclaude-codeService name tag on all signals
OTEL_RESOURCE_ATTRIBUTESComma-separated key=value resource tags

Logs

VariableDefaultDescription
OTEL_LOGS_EXPORT_INTERVAL5000Flush interval in milliseconds
OTEL_LOG_USER_PROMPTS0Set to 1 to include full prompt text
OTEL_LOG_TOOL_DETAILS0Set to 1 to include tool input parameters

Metrics

VariableDefaultDescription
OTEL_METRIC_EXPORT_INTERVAL60000Flush interval in milliseconds
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCEdeltadelta or cumulative
OTEL_METRICS_INCLUDE_SESSION_IDtrueInclude session.id label (increases cardinality)
OTEL_METRICS_INCLUDE_VERSIONfalseInclude app.version label
OTEL_METRICS_INCLUDE_ACCOUNT_UUIDtrueInclude user.account_uuid label

What you can do in Last9

Cost and spend tracking (metrics)

claude_code.cost.usage is a counter broken down by model and user.email. In Last9 Metrics, query the rate to get spend per minute, or sum it over a time window for daily/weekly totals:

  • Dashboard total team spend across all models
  • Break down by user.email to see per-developer cost
  • Alert when cumulative cost crosses a budget threshold

Token efficiency (metrics)

claude_code.token.usage tracks input, output, cache read, and cache creation tokens separately. Compare cacheRead / input ratio to measure prompt cache efficiency — a high ratio means less spend per interaction.

Session replay via prompt.id (logs)

Every log event shares a prompt.id UUID. Filter Last9 Logs by a specific prompt.id to reconstruct the full sequence for one user interaction:

user_prompt → api_request → tool_decision → tool_result → api_request

Useful for debugging slow sessions or unexpected tool rejections.

Tool usage audit (logs)

claude_code.tool_decision events record every permission decision with tool.decision = accept | reject and decision.source. Surface which tools are most frequently rejected and whether your --allowedTools policies are working.

Code output tracking (metrics)

claude_code.lines_of_code.count and claude_code.commit.count let you measure developer output attributable to Claude Code sessions — useful for adoption reporting.

Error rate monitoring (logs + alerts)

claude_code.api_error events include http.status_code and retry_attempt. Create a Last9 alert on error rate spikes to catch Claude API degradation before users report it.

Team-level tagging

For organizations with multiple teams, use OTEL_RESOURCE_ATTRIBUTES to tag sessions by team or project:

export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production,team=platform,project=infra-agent"

All signals from that session carry team and project labels, enabling per-team cost breakdowns in Last9.


Troubleshooting

No logs in Last9 after running Claude Code

  • Confirm CLAUDE_CODE_ENABLE_TELEMETRY=1 is set in the same shell session where you run claudeexport does not persist across tabs or new sessions
  • Set OTEL_EXPORTER_OTLP_PROTOCOL=http/json — Last9’s endpoint requires HTTP, not gRPC (the OTel SDK default)
  • Check that OTEL_LOGS_EXPORTER=otlp (not none or console)
  • Wait at least 10 seconds — the default export interval is 5s and there may be one flush delay

No metrics appearing

  • Set OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative — Claude Code defaults to delta temporality but Last9 requires cumulative counters
  • Set OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/json if the global protocol var is not being picked up
  • Metrics flush every 60 seconds by default — wait at least 90 seconds before checking
  • Metric names in Last9 use underscores: claude_code.cost.usage becomes claude_code_cost_usage_total

401 / authentication errors

  • Verify the header format: Authorization=Basic <token> (no extra quotes, no Bearer prefix)
  • Regenerate the token from Integrations → OpenTelemetry if it has expired

Events appear but user.email is missing

  • user.email is only populated when authenticated with a user account (not API key only)
  • Run claude --version to confirm you are logged in

Please get in touch with us on Discord or Email if you have any questions.