# Claude Code Metrics Setup: Prometheus + Grafana dashboards for tracking AI coding productivity

> Source: <https://gist.github.com/mikelane/9bf3053b5608df5858d299d636a48e8f>
> Published: 2025-12-12 21:19:55+00:00

# Claude Code Metrics Setup Guide

This guide walks through setting up OTEL telemetry from Claude Code to Prometheus + Grafana.

## Architecture

```
Claude Code  -->  OTLP/HTTP  -->  Prometheus  -->  Grafana
   (metrics)      (protobuf)      (storage)       (viz)
```

## 1. Environment Variables

Add to your shell profile (`~/.bashrc` for Linux, `~/.zshrc` for macOS):

``` bash
# 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=delta
```

Reload your shell: `source ~/.bashrc`

## 2. Prometheus Setup

### Install (AL2023 / Amazon Linux)

``` bash
# 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/data
```

### Install (macOS with Homebrew)

``` bash
brew install prometheus
```

### Configuration

Create `~/.prometheus/prometheus.yml`:

``` yaml
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
    - targets: ["localhost:9090"]
```

### Run Prometheus with OTLP Receiver

**Critical flags:**
- `--web.enable-otlp-receiver` - Enables the OTLP ingestion endpoint
- `--enable-feature=otlp-deltatocumulative` - Converts delta metrics (like lines_of_code) to cumulative

``` bash
# 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-deltatocumulative
```

You can run this in a tmux session or background it.

## 3. Grafana Setup

### Install (AL2023 / Amazon Linux)

``` bash
# 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-server
```

### Install (macOS)

``` bash
brew install grafana
brew services start grafana
```

### Access

- URL: http://localhost:3000
- Default login: admin / admin

### Add Prometheus Data Source

1. Go to Connections → Data Sources → Add data source
2. Select Prometheus
3. URL: `http://localhost:9090`
4. Save & Test

### Import Dashboards

1. Go to Dashboards → Import
2. Paste the JSON from the dashboard files in this directory
3. Select your Prometheus data source
4. 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)

## 4. Verify Metrics Are Flowing

After using Claude Code with telemetry enabled:

``` bash
# Check available metrics
curl -s 'http://localhost:9090/api/v1/label/__name__/values' | jq '.data[]' | grep claude
```

You should see:
- `claude_code_token_usage_tokens_total`
- `claude_code_cost_usage_USD_total`
- `claude_code_active_time_seconds_total`
- `claude_code_lines_of_code_count_total`
- `claude_code_session_count_total`

## 5. Available Metrics

| 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.) |

## 6. Key Queries

**Productivity Ratio** (CLI time / User time):
``` promql
sum(claude_code_active_time_seconds_total{type="cli"})
/ sum(claude_code_active_time_seconds_total{type="user"})
```

**Lines per Dollar**:
``` promql
sum(sum_over_time(claude_code_lines_of_code_count_total[$__range]))
/ sum(increase(claude_code_cost_usage_USD_total[$__range]))
```

**Cache Efficiency**:
``` promql
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()`:
``` promql
# 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]))
```

## 7. Troubleshooting

### No metrics showing up

1. Check env vars are set: `env | grep OTEL`
2. Verify Prometheus is running with OTLP flags: `ps aux | grep prometheus`
3. Check Prometheus logs for OTLP errors
4. Make sure you've used Claude Code after setting env vars (restart terminal first)

### Grafana can't connect to Prometheus

1. Verify Prometheus is running: `curl http://localhost:9090/-/healthy`
2. Check Grafana data source URL matches Prometheus listen address

### Dashboard shows no data

1. Check time range (try "Last 24 hours")
2. Verify metrics exist: `curl 'http://localhost:9090/api/v1/query?query=claude_code_token_usage_tokens_total'`
3. Make sure Prometheus data source is selected in dashboard

## 8. For Datadog (Alternative)

If pushing to Datadog instead of local Prometheus:

``` bash
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.

