This guide walks through setting up OTEL telemetry from Claude Code to Prometheus + Grafana.
Claude Code --> OTLP/HTTP --> Prometheus --> Grafana
(metrics) (protobuf) (storage) (viz)
Add to your shell profile (~/.bashrc for Linux, ~/.zshrc for macOS):
# Enable Claude Code telemetry
export CLAUDE_CODE_ENABLE_TELEMETRY=1
# OTEL export configuration
export OTEL_METRICS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:9090/api/v1/otlp
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=deltaReload your shell: source ~/.bashrc
# Download latest Prometheus
cd /tmp
curl -LO https://github.com/prometheus/prometheus/releases/download/v3.0.0/prometheus-3.0.0.linux-amd64.tar.gz
tar xzf prometheus-3.0.0.linux-amd64.tar.gz
sudo mv prometheus-3.0.0.linux-amd64 /opt/prometheus
# Create data directory
mkdir -p ~/.prometheus/databrew install prometheusCreate ~/.prometheus/prometheus.yml:
global:
scrape_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]Critical flags:
--web.enable-otlp-receiver- Enables the OTLP ingestion endpoint--enable-feature=otlp-deltatocumulative- Converts delta metrics (like lines_of_code) to cumulative
# Linux
/opt/prometheus/prometheus \
--config.file=~/.prometheus/prometheus.yml \
--storage.tsdb.path=~/.prometheus/data \
--web.listen-address=127.0.0.1:9090 \
--web.enable-otlp-receiver \
--enable-feature=otlp-deltatocumulative
# macOS (if running manually)
prometheus \
--config.file=/opt/homebrew/etc/prometheus.yml \
--storage.tsdb.path=/opt/homebrew/var/prometheus \
--web.listen-address=127.0.0.1:9090 \
--web.enable-otlp-receiver \
--enable-feature=otlp-deltatocumulativeYou can run this in a tmux session or background it.
# Add Grafana repo
sudo tee /etc/yum.repos.d/grafana.repo << 'EOF'
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF
sudo dnf install grafana -y
sudo systemctl start grafana-serverbrew install grafana
brew services start grafana- URL: http://localhost:3000
- Default login: admin / admin
- Go to Connections → Data Sources → Add data source
- Select Prometheus
- URL:
http://localhost:9090 - Save & Test
- Go to Dashboards → Import
- Paste the JSON from the dashboard files in this directory
- Select your Prometheus data source
- Import
Dashboard files included:
dashboard-metrics.json- Claude Code Metrics (tokens, cost, productivity ratio)dashboard-summary.json- Daily/Weekly Summary (aggregated stats)dashboard-economics.json- Engineering Economics (ROI, team equivalence)
After using Claude Code with telemetry enabled:
# Check available metrics
curl -s 'http://localhost:9090/api/v1/label/__name__/values' | jq '.data[]' | grep claudeYou should see:
claude_code_token_usage_tokens_totalclaude_code_cost_usage_USD_totalclaude_code_active_time_seconds_totalclaude_code_lines_of_code_count_totalclaude_code_session_count_total
| Metric | Labels | Description |
|---|---|---|
claude_code_token_usage_tokens_total |
type (input/output/cacheRead/cacheCreation), model |
Token consumption |
claude_code_cost_usage_USD_total |
model |
Estimated API cost in USD |
claude_code_active_time_seconds_total |
type (cli/user) |
Time tracking |
claude_code_lines_of_code_count_total |
type (added/removed) |
Code output (delta metric!) |
claude_code_session_count_total |
Session count | |
claude_code_event_count_total |
event |
Events (commits, PRs, etc.) |
Productivity Ratio (CLI time / User time):
sum(claude_code_active_time_seconds_total{type="cli"})
/ sum(claude_code_active_time_seconds_total{type="user"})
Lines per Dollar:
sum(sum_over_time(claude_code_lines_of_code_count_total[$__range]))
/ sum(increase(claude_code_cost_usage_USD_total[$__range]))
Cache Efficiency:
sum(claude_code_token_usage_tokens_total{type="cacheRead"})
/ (sum(claude_code_token_usage_tokens_total{type="cacheRead"}) + sum(claude_code_token_usage_tokens_total{type="input"}))
* 100
Important: The lines_of_code metric is a delta metric, not cumulative. Use sum_over_time() instead of increase():
# Correct
sum(sum_over_time(claude_code_lines_of_code_count_total[$__range]))
# Wrong - will give weird extrapolated values
sum(increase(claude_code_lines_of_code_count_total[$__range]))
- Check env vars are set:
env | grep OTEL - Verify Prometheus is running with OTLP flags:
ps aux | grep prometheus - Check Prometheus logs for OTLP errors
- Make sure you've used Claude Code after setting env vars (restart terminal first)
- Verify Prometheus is running:
curl http://localhost:9090/-/healthy - Check Grafana data source URL matches Prometheus listen address
- Check time range (try "Last 24 hours")
- Verify metrics exist:
curl 'http://localhost:9090/api/v1/query?query=claude_code_token_usage_tokens_total' - Make sure Prometheus data source is selected in dashboard
If pushing to Datadog instead of local Prometheus:
export OTEL_METRICS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.datadoghq.com
export OTEL_EXPORTER_OTLP_HEADERS="DD-API-KEY=your_api_key"The same metrics will flow to Datadog, and you can build similar dashboards there.