Claude CodeのOpenTelemetry設定—チームのトークン消費を可視化する

Claude CodeのOpenTelemetry設定—チームのトークン消費を可視化する | mohablog

Claude Code をチームに配ると、すぐ出てくるのが「誰がどれだけトークンを使っているのか」という問いですよね。これに答える仕組みが OpenTelemetry エクスポート。現行 v2.1 系(執筆時点 2.1.158)は、メトリクス・イベント・トレースの3系統を標準の OTel プロトコルで吐き出せます。

目次

Claude Codeのテレメトリで測れるもの

公式ドキュメントの Monitoring ページは、出力を「メトリクス(時系列)」と「イベント(ログ/events)」の2系統に分けています。トレースは beta 扱い。まず何が数値として落ちてくるかを押さえます。

メトリクスは8種類

セッション数・コード行数・コミット数・トークン・コストなどがカウンターとして出ます。名前は固定です。

メトリクス名 内容 単位
claude_code.session.count 開始したCLIセッション数 count
claude_code.lines_of_code.count 変更されたコード行数 count
claude_code.pull_request.count 作成したPR数 count
claude_code.commit.count 作成したgitコミット数 count
claude_code.cost.usage セッションのコスト USD
claude_code.token.usage 消費トークン数 tokens
claude_code.code_edit_tool.decision 編集ツールの許可判断回数 count
claude_code.active_time.total アイドルを除いた実働秒数 s

コストを見たいだけなら claude_code.cost.usage、内訳を追うなら claude_code.token.usage。後者は type 属性でさらに割れます(後述)。active_time.total がアイドルを除いた実働秒数を返す点は見落としがちで、セッション数だけでは分からない「実際に手を動かしていた時間」を別系列で取れます。

イベントはAPIリクエスト単位の明細

メトリクスが集計値なのに対し、イベントは1操作ごとの粒度。OTEL_LOGS_EXPORTER を設定すると、ユーザープロンプト・ツール実行結果・APIリクエスト・APIエラー・ツール許可判断といったイベントが流れます。ほかに認証・MCP接続・コンパクションなども対象で、種類は版を追うごとに増えています。たとえば claude_code.api_request には model / cost_usd / input_tokens / cache_read_tokens が乗る。どのモデルに何トークン投げたかを1リクエスト単位で追えます。

ツール実行の成否を追う

デバッグ用途で効くのが claude_code.tool_result イベント。tool_name に加えて success(“true”/”false”)、duration_ms、失敗時の種別 error_type が乗ります(全文の errorOTEL_LOG_TOOL_DETAILS=1 で有効化)。どのツールが遅いか、どこで落ちているかを後追いできる。

{
  "event.name": "tool_result",
  "tool_name": "Bash",
  "success": "false",
  "duration_ms": 4210,
  "error_type": "ShellError"
}

このイベントを集計すれば「タイムアウトが多いツール」「実行が長いツール」が並びます。APIエラーは別に claude_code.api_error として status_codeattempt(リトライ回数)付きで出るため、レート制限の頻度もここで掴めます。

console出力で吐き出しを確かめる

本番のバックエンドを用意する前に、手元の標準出力で中身を見ます。OTEL_METRICS_EXPORTER=console にすれば、収集器なしでレコードがそのまま見える。

# 1. テレメトリを有効化
export CLAUDE_CODE_ENABLE_TELEMETRY=1

# 2. まずは console で吐き出す
export OTEL_METRICS_EXPORTER=console
export OTEL_LOGS_EXPORTER=console

# 3. デバッグ中は間隔を詰める(本番では戻す)
export OTEL_METRIC_EXPORT_INTERVAL=10000  # 10秒(デフォルト 60000ms)
export OTEL_LOGS_EXPORT_INTERVAL=5000     # 5秒(デフォルト 5000ms)

claude

セッションを少し動かすと、設定した間隔ごとに標準出力へメトリクスのレコードが流れます。形はこうなります(値は環境で変わるため例)。

{
  "descriptor": { "name": "claude_code.token.usage", "unit": "tokens" },
  "dataPoints": [
    {
      "attributes": {
        "type": "input",
        "model": "claude-sonnet-4-5",
        "session.id": "b1f2c8d0-...",
        "user.account_uuid": "a9c3-...",
        "terminal.type": "vscode"
      },
      "value": 18432
    }
  ]
}

属性に modeltype が乗っているのが見えます。ここまで確認できれば、エンドポイントを otlp に切り替えるだけで Collector へ送れます。

otlpへ切り替えて収集器に送る

動作確認が済んだら exporter を otlp にして、エンドポイントと認証ヘッダを足します。プロトコルは grpc / http/json / http/protobuf から選択。

export OTEL_METRICS_EXPORTER=otlp
export OTEL_LOGS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer your-token"

claude

これで console に出ていたのと同じレコードが、指定した OTLP エンドポイントへ送られます。受け口は OpenTelemetry Collector でも、Datadog や Grafana のような OTLP 対応バックエンドでも構いません。Prometheus にスクレイプさせたい場合は exporter を prometheus に変えます。デフォルトのエクスポート間隔はメトリクス 60秒、ログ 5秒。デバッグ用に縮めたら、本番投入前に戻します。

チーム・コストセンター単位で割る

デフォルトの属性だけだと、組織全体の合算か、せいぜいアカウント単位までしか割れません。部署やプロジェクトでコストを按分したいなら、OTEL_RESOURCE_ATTRIBUTES に独自のラベルを足します。

# チーム識別用のカスタム属性を付与
export OTEL_RESOURCE_ATTRIBUTES="department=engineering,team.id=platform,cost_center=eng-123"

この指定を入れた状態でエクスポートすると、すべてのメトリクスとイベントの resource 部分に同じ属性が乗ります。

{
  "resource": {
    "attributes": {
      "service.name": "claude-code",
      "department": "engineering",
      "team.id": "platform",
      "cost_center": "eng-123"
    }
  }
}

あとはバックエンド側で cost_center ごとに claude_code.cost.usage を合計すれば、コストセンター単位の請求按分ができます。department でフィルタしてチーム別ダッシュボードを組むのも同じ理屈。配布時の環境変数を CI やシェルの初期化スクリプトに埋めておくと、利用者が意識せずタグ付きで送れます。

ここでやりがちなのが、利用者ごとに集計用のIDを手で持たせる発想。たとえば各自が USER_NAME=... のような独自変数を立てて後で名寄せする、といった運用です。これは表記揺れと付け忘れで簡単に壊れます。チーム軸は OTEL_RESOURCE_ATTRIBUTES で resource に固定し、個人軸は認証由来の user.account_uuid に任せる。役割を分けたほうが集計が安定します。

集計の軸になる標準属性

カスタム属性を足さなくても、認証済みなら以下が自動で付きます。組織やアカウントの単位はこれで足ります。

属性 内容 制御
session.id セッション識別子 OTEL_METRICS_INCLUDE_SESSION_ID(既定 true)
app.version Claude Codeのバージョン OTEL_METRICS_INCLUDE_VERSION(既定 false)
organization.id 組織UUID 認証時は常に付与
user.account_uuid アカウントUUID OTEL_METRICS_INCLUDE_ACCOUNT_UUID(既定 true)
terminal.type 端末種別(iTerm.app / vscode / cursor / tmux など) 検出時は常に付与

以前チームの利用状況を整理したとき、terminal.type が地味に役立ちました。誰が VS Code 拡張から、誰が素のターミナルから使っているかが分かり、導入支援の的を絞れたためです。

token.usageをtype属性で分解する

claude_code.token.usage は1つの数字ではありません。type 属性が input / output / cacheRead / cacheCreation の4値を取り、それぞれ別系列で出ます。プロンプトキャッシュがどれだけ効いているかは、ここを見ないと分かりません。

{
  "name": "claude_code.token.usage",
  "dataPoints": [
    { "attributes": { "type": "input", "model": "claude-sonnet-4-5" }, "value": 18432 },
    { "attributes": { "type": "cacheRead", "model": "claude-sonnet-4-5" }, "value": 152300 },
    { "attributes": { "type": "output", "model": "claude-sonnet-4-5" }, "value": 2741 }
  ]
}

上の例だと cacheReadinput を大きく上回っています。キャッシュ経由の読み出しが多いほど課金単価は下がるので、この比率はコスト最適化の手掛かりになる。claude_code.cost.usage はトークン消費とモデル価格から算出されるため、トークン側の内訳とコストは突き合わせて見ます。トークンを減らす運用そのものは Claude Codeのコンテキスト管理術サブエージェント並列実行のコスト管理 で扱っています。

カーディナリティと組織への配布

属性を増やすほど時系列の組み合わせ(カーディナリティ)が膨らみ、バックエンドのコストと負荷に跳ね返ります。とくに session.id はセッションごとに新しい値を取るため、放置すると系列数が際限なく増える。集計をユーザー単位やチーム単位で十分とするなら、無効化を検討します。

# 系列爆発を抑える: session.id を落とす
export OTEL_METRICS_INCLUDE_SESSION_ID=false

# バージョン別の分布を見たいときは逆に有効化
export OTEL_METRICS_INCLUDE_VERSION=true

設定後にエクスポートされるメトリクスから session.id が消え、app.version が追加されます。集計軸を絞った分だけ系列数が減る。組織全体へ同じ設定を強制するなら、各自の環境変数ではなく managed settings に書きます。

{
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.company.com:4317"
  }
}

このファイルは macOS なら /Library/Application Support/ClaudeCode/managed-settings.json に置きます(Linux は /etc/claude-code/、Windows は C:\ProgramData\ClaudeCode\)。MDM で配れば、利用者の手元設定に依存せず全台で同じテレメトリ構成を効かせられます。エンタープライズで認証トークンを動的に差し替えたい場合は otelHeadersHelper にスクリプトを指定でき、起動時とその後定期的(既定29分間隔)に呼ばれてトークンを更新できます。

運用前に踏むつまずき

有効化のあとで効いてくる設定を3点。

  • プロンプト本文は既定で送られないclaude_code.user_prompt イベントの prompt は伏字で、OTEL_LOG_USER_PROMPTS=1 を入れて初めて中身が乗ります。社外秘コードを扱う環境では安易に有効化しないこと。
  • トレースは betaOTEL_TRACES_EXPORTER で出せますが、公式が「スキーマは将来変わりうる」と明記しています。ダッシュボードを固く作り込むのはメトリクス・イベント側にとどめるのが無難です。
  • デバッグ用に詰めた間隔を戻すOTEL_METRIC_EXPORT_INTERVAL を10秒のままにすると、本番では送信回数が無駄に増えます。確認が済んだら既定の60秒へ。

よくある質問

サブスクリプション利用でもコストは出る?

出ます。claude_code.cost.usage はトークン消費とモデル価格から算出される推定値で、定額プランか従量課金かに関わらずメトリクスとして流れます。実際の請求額そのものではなく、利用量の目安として見るのが正しい使い方です。

メトリクスとイベントはどちらを使えばいい?

ダッシュボードでチーム別のコストや実働時間を追うならメトリクス。個々のツール失敗やAPIエラーを掘るならイベント。両方を otlp で同時に送り、ダッシュボードはメトリクス、障害調査はイベント、と参照先を分けます。

まとめ

  • CLAUDE_CODE_ENABLE_TELEMETRY=1 とエクスポータ指定だけで、トークン・コスト・実働時間が OTel で出る
  • まず OTEL_METRICS_EXPORTER=console で中身を確認し、本番は otlp に切り替える
  • チーム按分は OTEL_RESOURCE_ATTRIBUTEScost_center などを足す
  • token.usagetype 属性で input / output / cacheRead / cacheCreation に分解できる
  • session.id はカーディナリティが膨らむため、集計粒度に応じて無効化を検討する
  • 組織配布は managed-settings.json、プロンプト本文の送信は既定オフ
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次