Claude Code × OpenTelemetry実践ガイド — トークン消費・コスト・ツール実行をGrafanaでリアルタイム可視化する
Claude Codeを日常的に使っていると、ふと気になる瞬間がある。「今月いくら使ったんだろう?」「どのツール呼び出しがトークンを食っているのか?」——CLIの/costコマンドではセッション単位の確認しかできず、長期的なトレンドや異常検知には力不足だ。
実はClaude CodeにはOpenTelemetry(OTel)がネイティブ組み込みされており、環境変数を数行設定するだけでメトリクスとイベントをエクスポートできる。本記事では、個人開発者がGrafana Cloud無料枠だけで「トークン消費ダッシュボード」を構築し、コスト最適化のアクションにつなげるまでを一気通貫で解説する。
なぜClaude Codeにオブザーバビリティが必要なのか
/costコマンドの限界と定量モニタリングの価値
/costはそのセッションのトークン消費とコストを表示する便利なコマンドだが、セッションを閉じればデータは消える。日次・週次のトレンド分析や、「先週と比べてコストが倍増している」といった異常検知はできない。
一方、OTelメトリクスをバックエンドに蓄積すれば、「どのプロジェクトの、どのステージで、どのモデルがトークンを消費したか」をドリルダウンで分析できる。/insightsコマンドが過去セッションの定性的な振り返りからCLAUDE.md改善につなげるアプローチなのに対し、OTelは定量データによるリアルタイム監視という明確に異なるレイヤーをカバーする。
自律エージェント・CI/CD利用で特に重要な理由
個人の対話的な利用であれば「なんとなく高かったな」で済むかもしれない。しかし、自律開発デーモンやCI/CDパイプラインでClaude Codeを大量に回している場合、予算超過の予兆検知やレート制限の事前察知は運用上の必須要件になる。ダッシュボードがあるかないかで、障害対応のスピードが決定的に変わる。
Claude CodeのOTelテレメトリ全体像
エクスポートされるメトリクスとイベント
Claude Codeは以下の8つのメトリクスをエクスポートする。すべてCounter型だ。
| メトリクス名 | 説明 | 単位 | 主要属性 |
|---|---|---|---|
claude_code.token.usage | トークン消費量 | tokens | type(input/output/cacheRead/cacheCreation), model |
claude_code.cost.usage | セッションコスト | USD | model |
claude_code.session.count | CLI起動回数 | count | — |
claude_code.lines_of_code.count | コード変更行数 | count | type(added/removed) |
claude_code.pull_request.count | PR作成数 | count | — |
claude_code.commit.count | コミット作成数 | count | — |
claude_code.code_edit_tool.decision | コード編集ツールの許可判定 | count | tool_name, decision |
claude_code.active_time.total | アクティブ時間 | seconds | type(user/cli) |
加えて、5つのイベントがログプロトコル経由でエクスポートされる。
| イベント名 | 発火タイミング | 注目属性 |
|---|---|---|
claude_code.user_prompt | ユーザーがプロンプトを送信 | prompt_length |
claude_code.api_request | APIリクエスト発行 | model, cost_usd, input_tokens, output_tokens |
claude_code.api_error | APIエラー発生 | status_code, error |
claude_code.tool_decision | ツール実行の許可/拒否判定 | tool_name, decision |
claude_code.tool_result | ツール実行完了 | tool_name, duration_ms, success |
すべてのイベントにprompt.id(UUID v4)が付与されており、1つのプロンプトから派生したAPI呼び出し→ツール実行の因果関係をトレースできる。
アーキテクチャ
最小構成はCollectorを挟まないダイレクト送信だ。
Claude Code → (OTLP/HTTP) → Grafana Cloud (Mimir + Loki)
↓
Grafana Dashboard
フィルタリングやサンプリングが必要な場合はOTel Collectorを間に挟む構成もあるが、個人利用ならダイレクト送信で十分だ。
5分で始めるセットアップ(Grafana Cloud無料枠)
Step 1:環境変数の設定
シェルプロファイル(~/.zshrc等)に以下を追記する。
# Claude Code OTelテレメトリを有効化
export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
# Grafana Cloudエンドポイント(リージョンに応じて変更)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp-gateway-prod-ap-northeast-0.grafana.net/otlp"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic <base64token>"
# Prometheus系バックエンド向け:Cumulative temporalityに変更(重要!)
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative
# セッション別集計を行う場合に必要(デフォルトはfalse)
export OTEL_METRICS_INCLUDE_SESSION_ID=true
~/.claude/settings.jsonで設定する方法もあるが、正直シェルプロファイルに書くほうが管理しやすい。
Step 2:Grafana Cloudアカウント作成とOTLPエンドポイント取得
- Grafana Cloudで無料アカウントを作成
- ダッシュボードの Connections → Add new connection → OpenTelemetry (OTLP) を選択
- 表示されるエンドポイントURLとAPIトークンをコピー
<base64token>は<instance_id>:<api_token>をBase64エンコードした値
無料枠はメトリクス10,000シリーズ、ログ50GB/月。個人利用には十分すぎる容量だ。
Step 3:動作確認
source ~/.zshrc
claude -p "Hello, world"
実行後60秒ほど待ってから、Grafana Cloudの Explore 画面でPrometheusデータソースを選択し、以下のクエリを実行する。
claude_code_token_usage_tokens_total
データが表示されればセットアップ完了だ。表示されない場合は、後述の「ハマりポイント」セクションを確認してほしい。
Grafanaダッシュボード構築 — 4つの必須パネル
パネル1:トークン消費量の時系列推移(input/output別)
最も基本的なパネル。モデル×トークンタイプ別の消費量推移を可視化する。
# input tokens per model(5分間のレート)
sum(rate(claude_code_token_usage_tokens_total{type="input"}[5m])) by (model)
# output tokens per model
sum(rate(claude_code_token_usage_tokens_total{type="output"}[5m])) by (model)
# cache read tokens(コスト削減に直結する指標)
sum(rate(claude_code_token_usage_tokens_total{type="cacheRead"}[5m])) by (model)
Time Seriesパネルでtypeごとに色分けすると、inputとoutputの比率が一目でわかる。
パネル2:累積コスト&日別コストバー
# 累積コスト(Stat パネル向け)
sum(claude_code_cost_usage_usd_total)
# 日別コスト(Bar chart パネル向け)
sum(increase(claude_code_cost_usage_usd_total[1d])) by (model)
Statパネルで「今月の累積コスト」を大きく表示し、その下にBar chartで日別推移を並べるレイアウトがおすすめだ。
パネル3:ツール呼び出しヒートマップ
ツール呼び出しパターンの分析にはLokiに蓄積されたイベントデータを使う。
# ツール別の呼び出し回数(Lokiクエリ)
sum by (tool_name) (
count_over_time(
{service_name="claude-code"} | json | event_name="claude_code.tool_result" [1h]
)
)
Read、Edit、Bash、Grepといったツール別の利用頻度が可視化される。特定のツールが異常に多く呼ばれていれば、プロンプトやCLAUDE.mdの見直しシグナルになる。
パネル4:セッション別コストランキング
コスト爆発の「犯人」を特定するパネル。
注意: このパネルを利用するには、環境変数 OTEL_METRICS_INCLUDE_SESSION_ID=true の設定が必要だ。デフォルトではカーディナリティ抑制のためsession_id属性はメトリクスに含まれない。Step 1の環境変数設定で追加済みであることを確認してほしい。
# セッション別の総コスト(Top 10)
topk(10, sum by (session_id) (claude_code_cost_usage_usd_total))
コスト上位のセッションを特定したら、そのセッションIDでLokiイベントをフィルタし、どのツール呼び出しがトークンを消費したかをドリルダウンする。
{service_name="claude-code"} | json | session_id="<対象のsession_id>"
なお、Grafanaダッシュボードは Dashboard settings → JSON Model からエクスポートできる。チームで共有する場合はJSONをリポジトリに含めておくとよい。
ハマりポイントと対処法
Delta temporalityのサイレントドロップ問題
これが最大の罠だ。Claude CodeはデフォルトでDelta temporalityでメトリクスを送出する。しかしPrometheusやVictoriaMetricsはCumulative前提で設計されているため、Deltaで送られたデータはエラーも出さずに静かに捨てられる。
# 必ず設定すること
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative
「セットアップしたのにGrafanaにデータが出ない」という場合、まずこの設定を確認してほしい。
Prometheus命名規則の不一致
OTel標準ではドット区切り(claude_code.token.usage)だが、Prometheusに格納される際にアンダースコア区切り+型サフィックスに変換される(claude_code_token_usage_tokens_total)。PromQLを書くときは変換後の名前を使う必要がある。
Grafana CloudのExplore画面でメトリクス名を検索する際、claude_codeでオートコンプリートすれば実際に格納されている名前を確認できる。
OTel Collectorを挟む場合
フィルタリングやマルチバックエンド送信が必要な場合は、OTel Collectorを間に入れる。Anthropic公式のclaude-code-monitoring-guideリポジトリにDocker Compose構成が公開されているので参考にするとよい。
# docker-compose.yml(抜粋)
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
volumes:
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
ports:
- "4318:4318" # OTLP HTTP
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
# otel-collector-config.yaml(抜粋)
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
exporters:
prometheusremotewrite:
endpoint: "http://prometheus:9090/api/v1/write"
service:
pipelines:
metrics:
receivers: [otlp]
exporters: [prometheusremotewrite]
logs:
receivers: [otlp]
exporters: [loki]
ローカルで完結させたい場合はこの構成が便利だが、まずはGrafana Cloudダイレクト送信で始めることを推奨する。
コスト最適化アクション — ダッシュボードから改善へ
データから読み取る3つの改善シグナル
ダッシュボードを眺めるだけでは意味がない。以下の3つのシグナルを見つけたらアクションにつなげよう。
シグナル1:特定ツールの過剰呼び出し。ReadやGrepが異常に多い場合、Claude Codeが必要な情報を見つけられずに探索を繰り返している可能性がある。CLAUDE.mdにプロジェクト構造やファイルの役割を明記することで、無駄な探索を減らせる。
シグナル2:input token比率が異常に高い。outputに対してinputが圧倒的に多い場合、プロンプトの肥大化か、不要なファイルの読み込みが原因であることが多い。.claudeignoreで不要なディレクトリ(node_modules、ビルド成果物、大量のデータファイル等)を除外しよう。
シグナル3:特定セッションだけコスト突出。自律実行でコストが暴走したケースだ。--max-turnsによる最大ターン数制限と、Grafanaアラートによるコスト上限通知を組み合わせることで安全装置とする。
Grafanaアラートでコスト上限を通知する
Grafana Cloudでアラートルールを設定し、日次コストが閾値を超えたらSlackに通知する例を示す。
# アラート条件:1日のコストが$5を超えたら発火
sum(increase(claude_code_cost_usage_usd_total[1d])) > 5
Grafanaの Alerting → Alert rules → New alert rule から、上記のPromQL条件を設定し、Contact pointにSlack Webhookを追加する。自律エージェントを回している場合、このアラートがないと安心して眠れない。
まとめ
Claude Codeの組み込みOTel機能とGrafana Cloud無料枠を組み合わせれば、個人開発者でも「トークンをどこで・なぜ・いくら消費しているか」をリアルタイムに把握できる。特に自律エージェントやCI/CDで大量に回している場合、ダッシュボードによる異常検知とコストアラートは必須の安全装置だ。
まずは環境変数の設定から始めて、データが溜まったらダッシュボードを育てていこう。定量データに基づくフィードバックループが、AIコーディングのROIを確実に引き上げてくれるはずだ。
