Claude Code のテレメトリを Grafana otel-lgtm で可視化してみた

Claude Code のテレメトリを Grafana otel-lgtm で可視化してみた

Claude CodeのOpenTelemetryテレメトリを、ローカルのgrafana/otel-lgtmで可視化してみました。環境構築からデータ分析まで、実装手順と予想外の発見(指定していないはずのhaikuが最多リクエストだった理由など)をまとめています。
2026.07.21

はじめに

こんにちは!ひろたこです。
今回は Claude Code の OpenTelemetry(OTel)テレメトリを、ローカルに立てた grafana/otel-lgtm オールインワンコンテナで丸ごと可視化してみました!

Claude Code を毎日使っていて、「今日いくら使った?」「何のリクエストにいくらかかってる?」と聞かれて即答できますか?私はできませんし、正直サブスクなので気にしていませんでした。Claude Code は環境変数を数個設定するだけでメトリクス・ログ・トレースの3シグナルを OTLP でエクスポートできるので、「中で何が起きているか」を実際に覗いてみました!

この記事でわかることは次の3つです。

  • Colima + grafana/otel-lgtm で、Docker Desktop なしにテレメトリ可視化環境を作る手順
  • Grafana(Tempo / Loki / Prometheus)で Claude Code の3シグナルをどう見るか
  • 実データ分析で見つけた落とし穴:頼んでいないはずの haiku が最多リクエストだった理由と、モデル比較に query_source フィルタが必須な話

Claude Code のテレメトリとは

Claude Code には OpenTelemetry 準拠のテレメトリ機能が組み込まれており、環境変数だけで有効化できます。コードの改修やプラグインの導入は不要です。

  • メトリクス・ログ(イベント)は CLAUDE_CODE_ENABLE_TELEMETRY=1 で有効化
  • トレースはベータ機能で、CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 を追加で指定
  • デフォルトではプロンプト本文やツールの中身は送信されません(イベント名・モデル名・トークン数などの構造情報のみ)。内容まで記録したい場合だけ、専用の環境変数でオプトインする設計です

詳細は公式ドキュメントにまとまっています。

有効化に関する詳細な記事はこちらです。

https://dev.classmethod.jp/articles/building-claude-code-audit-log-with-opentelemetry/

grafana/otel-lgtm とは

grafana/otel-lgtm は、Grafana が提供する検証用オールインワンコンテナです。コンテナ1つの中に以下が全部入っています。

コンポーネント 役割
OTel Collector OTLP でテレメトリを受信
Prometheus メトリクスの保存
Loki ログの保存
Tempo トレースの保存
Grafana 可視化 UI

grafana-otel-lgtm

(図は grafana/docker-otel-lgtm 公式リポジトリより引用)

「OTel の3シグナルを受けて見る」ための環境が docker run 一発で手に入るので、今回のような検証にぴったりです。

環境・前提条件

検証は 2026年7月20日〜21日に実施しました。

項目 バージョン等
マシン macOS 26.5.2(Apple Silicon / darwin/arm64)
Colima 0.10.3(依存: lima 2.1.4)
Docker CLI(Mac 側) 29.6.2
Docker Engine(Colima VM 内 / linux/arm64) 29.5.2
docker-compose 5.3.1
grafana/otel-lgtm latest(digest sha256:b4da8bf6b1b44f6f683883869619eb9781465c642ce5f2d70591fb27094ddb1c
Claude Code 2.1.215

やったこと

1. Colima と Docker CLI のインストール

まずは Homebrew で Colima・Docker CLI・docker-compose をインストールします。

$ brew install colima docker docker-compose

私の環境ではインストール完了まで約2分でした。Docker Desktop を入れなくても、これだけで Docker 環境の材料が揃います。

2. Colima の起動

VM を起動します。CPU とメモリは控えめに指定しました。

$ colima start --cpu 2 --memory 4
INFO[0000] starting colima
INFO[0000] runtime: docker
INFO[0000] creating and starting ...                     context=vm
INFO[0001] downloading disk image ...                    context=vm
INFO[0055] provisioning ...                              context=docker
INFO[0057] starting ...                                  context=docker
INFO[0057] done

初回は VM のディスクイメージ取得が入りますが、実測では start 完了まで約1分でした。CPU/メモリの指定は初回だけ記憶されるので、次回以降は colima start だけで OK です。

動作確認は docker version で行います。ここでのポイントは、Client(Mac 側 CLI 29.6.2)と Server(VM 内エンジン 29.5.2)の2ブロックが両方表示されれば成功、ということです。Client 側の Context: colima が接続先の印になっています。Colima が未起動だと Server ブロックが「Cannot connect to the Docker daemon」エラーになるので、切り分けもしやすいです。

3. otel-lgtm コンテナの起動

いよいよ otel-lgtm を起動します。

$ docker run -d --name lgtm \
    -p 3000:3000 -p 4317:4317 -p 4318:4318 \
    grafana/otel-lgtm

公開しているポートの役割は以下の通りです。

ポート 用途
3000 Grafana UI
4317 OTLP gRPC 受信口
4318 OTLP HTTP 受信口

起動できたかログで確認します。

$ docker logs lgtm 2>&1 | tail -5
 - 4318: OpenTelemetry HTTP endpoint
 - 3000: Grafana (http://localhost:3000). User: admin, password: admin
 - 3200: Tempo endpoint
 - 4040: Pyroscope endpoint
 - 9090: Prometheus endpoint

ログに「The OpenTelemetry collector and the Grafana LGTM stack are up and running.」と出れば準備完了です。http://localhost:3000 を開くと Grafana にアクセスできます(初期ユーザー・パスワードは admin / admin)。

4. Claude Code のテレメトリ設定

Claude Code 側の設定は環境変数だけです。otel-claude.env というファイルにまとめました。全文貼っておきます。

# テレメトリ有効化(必須)
export CLAUDE_CODE_ENABLE_TELEMETRY=1
# トレース(ベータ)有効化 — 今回の主目的
export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1

# 3シグナルすべて OTLP でエクスポート
export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

# 検証用: エクスポート間隔を短縮(すぐ Grafana に反映される)
export OTEL_METRIC_EXPORT_INTERVAL=5000
export OTEL_LOGS_EXPORT_INTERVAL=1000
export OTEL_TRACES_EXPORT_INTERVAL=1000

# --- 以下はコンテンツ送信のオプトイン(必要なときだけコメントを外す) ---
# export OTEL_LOG_USER_PROMPTS=1        # プロンプト本文を含める
# export OTEL_LOG_ASSISTANT_RESPONSES=1 # モデル応答テキストを含める
# export OTEL_LOG_TOOL_DETAILS=1        # ツール入力引数を含める

エクスポート間隔をデフォルトから短縮(メトリクス5秒・ログ/トレース1秒)しているのは検証用の設定です。反映をすぐ確認できるほか、短命なプロセスがエクスポート前に終了してデータが欠落するのを防ぐ意味もあります。

使い方は source してから Claude Code を起動するだけです。

$ source ./otel-claude.env && claude

5. テレメトリ有効で同一タスクを3モデルで実行

テレメトリを有効にした状態で、同じタスクを model = fable / opus / sonnet の3セッション(対話モード)で実行しました。

  • タスク: 「Zed エディタの使い方を調べてみて簡潔に分かりやすくまとめてください。」

claude-code-diffrent-model-invoke

ちょっとした検証テクニックですが、ツールの許可プロンプトをわざと少し待ってから許可すると、claude_code.tool.blocked_on_user スパン(権限確認の待ち時間)が育って、トレースのウォーターフォールが見応えのあるものになります。

6. Grafana で3シグナルを確認する

Grafana の Explore から、シグナルごとにデータソースを切り替えて確認します。

シグナル 見方
トレース Explore → Tempo → Search で claude_code.interaction を検索 → ウォーターフォールで1ターンの内訳を見る
ログ(イベント) Explore → Lokitool_decision / user_prompt などのイベントを検索
メトリクス Explore → Prometheusclaude_code_token_usage などで検索

Tempo のウォーターフォールはこんな感じです。

grafana-tempo-view

ウォーターフォールは棒の左端が開始タイミング、長さが所要時間を表します。並行実行しているところと、何かを待っているところが視覚的にわかるのが気持ちいいです!

Claude Code から出てくる主なテレメトリは以下の通りです。

  • メトリクス: claude_code.session.count / claude_code.token.usage / claude_code.cost.usage / claude_code.lines_of_code.count / claude_code.commit.count / claude_code.active_time.total
  • イベント(ログ): claude_code.user_prompt / claude_code.api_request / claude_code.api_error / claude_code.tool_result / claude_code.tool_decision / claude_code.mcp_server_connection など
  • スパン(ベータ): claude_code.interactionclaude_code.llm_request / claude_code.tool(→ blocked_on_user / execution

見えたもの(分析)

環境ができたので、収集したデータを分析していきます。ここからが本番です!

分析1: モデル別の api_request 集計

Loki に届いた api_request イベントをセッション全体で集計しました。

grafana-loki

モデル リクエスト数 合計コスト API合計時間 出力トークン キャッシュ読取
claude-fable-5 8回 $0.758 69.2s 3,044 256,714
claude-opus-4-8 4回 $0.243 35.6s 1,868 110,623
claude-haiku-4-5 11回 $0.219 79.1s 6,036 0
claude-sonnet-5 6回 $0.216 34.4s 2,119 249,871

リクエスト単位のコスト・所要時間・トークン数がここまで見えるだけでも十分収穫なのですが…この表、何かおかしくないですか?

私は haiku にタスクを1つも与えていません。 実行したのは fable / opus / sonnet の3セッションだけです。それなのに、リクエスト数の最多は haiku の11回。しかもキャッシュ読取は0です。

分析2: 頼んでいない haiku が最多リクエストだった理由

正体を突き止めるため、api_request イベントに付いている query_source 属性でクロス集計してみました。

モデル query_source 回数 意味
fable-5 repl_main_thread 6 本体のタスク処理
sonnet-5 repl_main_thread 5 本体のタスク処理
opus-4-8 repl_main_thread 3 本体のタスク処理
haiku-4-5 web_search_tool 6 WebSearch ツールの内部処理
haiku-4-5 generate_session_title 3 セッションタイトル自動生成
haiku-4-5 web_fetch_apply 2 WebFetch の内容処理
各モデル away_summary / prompt_suggestion 各1 補助処理

謎が解けました。haiku の11回はすべて Claude Code 内部の補助呼び出しで、タスクは1回も処理していません。 WebSearch の内部処理に6回、セッションタイトルの自動生成に3回、WebFetch の内容処理に2回。Claude Code は、ユーザーが指定したモデルとは別に、ツールの内部処理やタイトル生成などで haiku を自動的に使っていたわけです。

これはテレメトリの生データを眺めていなければ気づけなかった発見でした。そしてここから重要な教訓が導けます。

モデル同士を比較したいなら、query_source="repl_main_thread" でのフィルタが必須です。フィルタせずに集計すると、内部の補助呼び出しが混ざって比較が歪みます。

Loki でそのまま使える LogQL クエリを置いておきます。まず本体リクエストだけを抽出するクエリです。

{service_name="claude-code"} | event_name="api_request" | query_source="repl_main_thread"

モデル別の時系列集計はこちらです。

sum by (model) (count_over_time({service_name="claude-code"}
  | event_name="api_request" | query_source="repl_main_thread" [5m]))

grafana-loki-model

ちなみに、本体リクエストの query_source は実行形態によって変わります。対話モードは repl_main_thread、ヘッドレス実行(claude -p)は sdk になるので、集計時は自分の実行形態に合わせてください。

まとめると、評価は2段構えにするのがよさそうです。

  • モデル自体の比較: repl_main_thread のみで集計する(純粋な本体の働きを見る)
  • コスト比較: セッション合計で集計する(内部呼び出しも「そのモデルに任せたときの総費用」に含まれるため)

分析3: 補正後の本体リクエスト数

repl_main_thread だけに絞った「本体の手数」はこうなりました(各モデル1試行)。

モデル 手数(本体リクエスト数)
opus-4-8 3回
sonnet-5 5回
fable-5 6回

opus が最少手数でタスクを終えています。ただし各モデル1試行のみなので、この差がモデルの実力なのかばらつきなのかは区別できません。ここから結論を出すのは早計です。この「ちゃんと比較したい」という欲求が、次の検証(モデル評価実験)につながります。

分析4: そのほかに見えたもの

権限判断(tool_decision イベント) も面白かったです。今回のセッションでは、WebFetch 7回・WebSearch 6回の計13回すべてが「その場で手動許可(user_temporary)」でした。つまりこの2つのツールは allowlist への追加候補だと、データが教えてくれています。一方 ToolSearch の3回は config(設定で自動許可)でした。テレメトリがそのまま設定改善の入口になる実用例ですね。

トレースは計8件収集できました(ルートスパン claude_code.interaction が5件、claude_code.llm_request が3件)。interaction の下に llm_requesttool がぶら下がり、tool の下に blocked_on_user(権限確認待ち)と execution(実行)が分かれる構造で、「1ターンの時間がどこに消えているか」を追える形になっています。スパンの詳細な時間分析は続編でやる予定です。

ハマったポイント

今回もいくつかハマったので正直に共有します。同じことを試す方の参考になれば!

再開時に docker run を再実行して Conflict エラー

検証を再開しようとして docker run をもう一度実行したら、エラーになりました。

The container name "/lgtm" is already in use

docker run新規作成コマンドなので、停止中の既存コンテナと名前が衝突します。分かっていたつもりでも、久しぶりに再開するとうっかりやりがちですww 2回目以降は docker start lgtm が正解です。既存コンテナの再開なので、収集済みのデータもそのまま復活します。

再開まわりの運用コマンドをまとめておきます。

# 一時中断(データを残す)
colima stop
# 再開
colima start        # CPU/メモリ設定は記憶済み
docker start lgtm   # 既存コンテナの再開
# 完全撤収(データも消える)
docker rm -f lgtm && colima stop

docker logs は停止中のコンテナでも表示される

docker logs lgtmコンテナが停止していても過去ログを表示します。ログが見えたから起動している、とは限りません。「ログは出るのにデータが来ない」ときはまずここを疑って、起動確認は docker ps の STATUS で行いましょう。

Colima 再起動でコンテナは自動復帰しない

Colima を停止するとコンテナは Exited (255) になり、colima start してもコンテナは自動再開しません(restart policy が no のため)。再開のたびに docker start lgtm が毎回必要です。

メトリクスが Prometheus に届かない(未解決)

今回一番悩ましかったのがこれです。メトリクスだけが Prometheus で検索できませんでした。

切り分けたところ、Collector までは届いている(受信176ポイント)ものの、Collector → Prometheus の転送が649ポイント全件失敗しており、claude_code_* 系のメトリクスが Grafana から見えない状態でした。

幸い、トークン量やコストは Loki 側の api_request イベントから取得できるため、検証自体は継続できました(分析1〜3はすべて Loki ベースです)。切り分け候補としては、OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=cumulative を env に追加しての再実行を考えています。この件は未解決のままなので、解決できたら追記します。

そのほかの注意点

  • otel-lgtm はデータをコンテナ内に持つため、docker rm すると収集データも消えます。検証用途なら許容範囲ですが、消したくないデータがあるうちは docker stop / docker start で運用しましょう
  • ヘッドレス実行のような短命プロセスでは、エクスポート間隔より早くプロセスが終了するとデータが欠落しえます。手順4の env でエクスポート間隔を短縮(メトリクス5秒・ログ/トレース1秒)しているのはこの対策も兼ねています

結果

3シグナルで何がどこまで見えたか、まとめます。

シグナル バックエンド 今回見えたもの
ログ(イベント) Loki api_request(モデル・コスト・所要時間・トークン・query_source)、tool_decision(権限判断13回の内訳)、user_prompt など。今回の分析の主戦場
トレース(ベータ) Tempo claude_code.interaction 配下のウォーターフォール(計8件)。blocked_on_user で権限確認の待ち時間も分離して見える
メトリクス Prometheus 今回は Collector → Prometheus の転送失敗により未確認(コスト・トークンは Loki の api_request で代替可能)

まとめ

今回は Claude Code のテレメトリを grafana/otel-lgtm で可視化し、実データを分析してみました!

わかったこと:

  • Claude Code のテレメトリは環境変数だけで有効化でき、grafana/otel-lgtm なら docker run 一発で受け皿が作れる。思っていたよりずっと手軽でした
  • Claude Code は指定モデルとは別に、内部の補助処理(WebSearch・タイトル生成など)で haiku を自動使用する。今回のデータでは haiku の11リクエストすべてが内部呼び出しでした
  • そのためモデル比較には query_source="repl_main_thread" フィルタが必須。コスト比較はあえてフィルタせずセッション合計で見る、という2段構えがおすすめです
  • tool_decision イベントから allowlist 追加候補(今回なら WebFetch と WebSearch)がデータで分かる。テレメトリは設定チューニングの入口にもなります
  • メトリクスの Prometheus 転送は未解決の課題として残りました。Loki のイベントで代替できたとはいえ、ここは追って解決したいところです

個人的に一番の収穫は、「頼んでいない haiku が最多リクエスト」という違和感から query_source にたどり着いた過程でした。集計表を鵜呑みにせず属性で掘る、という可観測性の基本を Claude Code 自身を題材に体験できたのは面白かったです。

そして分析3で書いた通り、各モデル1試行では手数の差から結論は出せません。**次回はテレメトリ×LLM採点でモデル比較を客観化する予定です。**続編もぜひ読んでいただけると嬉しいです!

この記事が誰かの助けになれば幸いです。
最後まで読んでいただき、誠にありがとうございました。

参考記事


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AI白書

関連記事