Claude Code のテレメトリで会話本文・API・ツール入出力をローカルに出してみた

Claude Code のテレメトリで会話本文・API・ツール入出力をローカルに出してみた

2026.06.17

Claude Code は OpenTelemetry でメトリクスやイベントを出せます。公式ドキュメントにイベントやメトリクスの一覧はあるのですが、実際の出力サンプルは見当たりませんでした。どんな形で、どこまで出るのか手元で見たくなったので試してみました。

使うのは OpenTelemetry の console エクスポーターだけです。Collector も SIEM も立てず、Claude Code がモデルとやり取りしている中身(入力したプロンプトや API のリクエスト/レスポンス本文など)をローカルのターミナルに出します。

どこまで見えるか

console エクスポーターで出せるのは、メトリクスやイベント名だけではありません。環境変数を足していくと、出力の中身が段階的に増えます。

入力プロンプトの全文、Messages API のリクエスト本文(会話履歴・system prompt・tools 一式)、レスポンス本文(assistant の応答テキスト)、ツールの入出力まで、手元のターミナルへ出てきます。

Collector + Grafana で可視化する構成はよく見かけますが、ここではもっと手前の「ローカルで会話本文を目視する」ところを見ます。監査やログ保全の要件を検討するときに、どの粒度で何が残せるかを自分で確かめられます。

前提

テレメトリは既定で無効です。CLAUDE_CODE_ENABLE_TELEMETRY=1 が起点になります。

プロンプト本文・API 本文・ツール詳細は、既定ではマスク(<REDACTED>)されるか、そもそも出力されません。中身を出すには専用の環境変数で明示的に有効化します。挙動は検証日時点のものです。仕様は変わり得るので、公式ドキュメントも併せて確認してください。

出力サンプルは headless 実行(claude -p "...")から1ターンずつ採りました。対話セッションで claude を起動しても同じイベントが出ます(対話時の注意は後述します)。

実機で事前に押さえておくと良い点が2つあります。

出力は1行1JSONではなく、Node の inspect 形式(body: "claude_code.xxx" / attributes: { ... } というオブジェクト整形)で出ます。目視には十分ですが、jq で直接パースはできません。プログラムで処理したいなら、後述の OTLP(OpenTelemetry の標準送信プロトコル)で Collector に送る方法を使います。

すべてのイベントの attributes に識別子が乗ります(user.email / user.id / session.id / organization.id / user.account_uuid / user.account_id)。この記事では <USER_EMAIL> などのプレースホルダーに置き換えて表示しています。ログを貼って共有するときは要注意です。

console エクスポーターでプロンプト本文を出す

Claude Code がそのままターミナルにイベントを吐きます。claude を起動する前に、テレメトリ有効化・console エクスポーター・OTEL_LOG_USER_PROMPTS をまとめて設定します。

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

# console エクスポーターに切り替え(ログ/メトリクスをターミナルへ)
export OTEL_LOGS_EXPORTER=console
export OTEL_METRICS_EXPORTER=console

# プロンプト本文を出す(既定はマスク)
export OTEL_LOG_USER_PROMPTS=1

# すぐ流れてくるようにエクスポート間隔を短縮(デバッグ用)
export OTEL_METRIC_EXPORT_INTERVAL=5000
export OTEL_LOGS_EXPORT_INTERVAL=1000

# headless で1ターン実行(console 出力はその場で stdout に出る)
claude -p "pongと返答して"

この状態で claude -p を実行すると、claude_code.user_promptclaude_code.api_request といったイベントが stdout に出ます。claude_code.user_promptprompt には、入力した文字列がそのまま入ります。

claude_code.user_prompt(OTEL_LOG_USER_PROMPTS=1)
  body: "claude_code.user_prompt",
  attributes: {
    "user.email": "<USER_EMAIL>",
    "organization.id": "<ORGANIZATION_ID>",
    "session.id": "<SESSION_ID>",
    "event.name": "user_prompt",
    "prompt.id": "<PROMPT_ID>",
    prompt_length: "9",
    prompt: "pongと返答して",
  },

OTEL_LOG_USER_PROMPTS を設定しない既定だと、同じイベントでも prompt<REDACTED> になります(prompt_length は出ます)。

claude_code.user_prompt(デフォルト)
    prompt_length: "9",
    prompt: "<REDACTED>",

中身が取れないのではなく、既定でマスクされているだけ、という違いです。

API リクエスト/レスポンス本文を出す(OTEL_LOG_RAW_API_BODIES)

OTEL_LOG_RAW_API_BODIES を立てると、Messages API のリクエストとレスポンスの本文がまるごとイベントに乗ります。

# インライン(イベントの body に直接埋め込む。60KB を超えると途中で切り詰められる)
export OTEL_LOG_RAW_API_BODIES=1

claude_code.api_request_bodyclaude_code.api_response_body というイベントが出ます。公式ドキュメントの表現では、リクエスト本文は会話履歴(entire conversation history)を含みます。body に system prompt / messages / tools などリクエストパラメータ一式が入り、body_length は切り詰め前の本来の長さです。

claude_code.api_request_body(OTEL_LOG_RAW_API_BODIES=1)
    "event.name": "api_request_body",
    body: "{\"model\":\"claude-opus-4-8\",\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"...会話本文...\"}]}],\"tools\":[...]}",
    body_length: "96553",
    body_truncated: "true",
    model: "claude-opus-4-8",

レスポンス側には、assistant の応答本文がそのまま入ります。今回は「pongと返答して」という指示なので、content{"type":"text","text":"pong"}stop_reasonusage も乗っています。

claude_code.api_response_body(OTEL_LOG_RAW_API_BODIES=1)
    "event.name": "api_response_body",
    body: "{\"model\":\"claude-opus-4-8\",\"id\":\"<MESSAGE_ID>\",\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"pong\"}],\"stop_reason\":\"end_turn\",\"usage\":{...}}",
    body_length: "786",
    request_id: "<REQUEST_ID>",

インライン(=1)では body が 60KB を超えると途中で切り詰められ、body_truncated: "true" が付きます。上のリクエストは body_length が 96553 ですが、body に実際に入るのは 60KB までです。全文が要るなら次の file:<dir> を使います。

会話履歴は本文ごと残りますが、extended thinking(思考ブロック)は対象外です。公式ドキュメントには、リクエスト本文・レスポンス本文のいずれでも extended-thinking content は redacted と記載があります(今回の単発検証では thinking が発生せず、この挙動自体は実演していません)。

本文を丸ごと(切り詰めなし)ファイルに残す(file:<dir>)

OTEL_LOG_RAW_API_BODIES にディレクトリを渡すと、本文をファイルに書き出します。

export OTEL_LOG_RAW_API_BODIES=file:/path/to/dir

このときイベントは body の代わりに body_ref(保存ファイルへの絶対パス)を持ちます。

claude_code.api_request_body(OTEL_LOG_RAW_API_BODIES=file:./bodies)
    body_ref: "<DIR>/<UUID>.request.json",
    body_length: "104987",

保存されるファイルは、リクエストが UUID 名で切り詰めなしの全文、レスポンスが request_id 名です。

ls ./bodies
<UUID>.request.json        104987 bytes(切り詰めなしの全文。inline の body_length 96553 と対照)
<REQUEST_ID>.response.json    786 bytes

response.json の中身は通常の Messages API レスポンス JSON で、content に assistant の応答テキストが入ります。実際にどの粒度の会話履歴が残るのかを確認・共有したいときに向いています。

ツールの入出力まで見たい場合

ツールの入出力も出せます。ただし console / logs 構成では、有効化する環境変数を取り違えやすいので注意します。

OTEL_LOG_TOOL_CONTENT はトレース(CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1OTEL_TRACES_EXPORTER)が前提で、console / logs だけでは効きません。logs 経路でツール詳細を見たいなら OTEL_LOG_TOOL_DETAILS=1 です。claude_code.tool_result イベントに tool_input / tool_parameters が乗ります。

claude_code.tool_result(OTEL_LOG_TOOL_DETAILS=1)
    "event.name": "tool_result",
    tool_name: "Bash",
    success: "true",
    tool_parameters: "{\"bash_command\":\"echo\",\"full_command\":\"echo hello-otel\",\"description\":\"Echo test string\"}",
    tool_input: "{\"command\":\"echo hello-otel\",\"description\":\"Echo test string\"}",

OTEL_LOG_TOOL_DETAILS を設定しない既定だと、tool_input / tool_parameters は出ません。OTEL_LOG_TOOL_CONTENT と混同しないよう注意してください。

対話セッションでライブ確認する(ローカル Collector)

console エクスポーターは headless(-p)やパイプ向きです。対話で claude を起動すると TUI が端末を専有し、console 出力はセッションを抜けるまで表示されません(抜けた瞬間にまとめて出てきます)。

対話を続けながらライブで追うには、ログの出口を TUI と別プロセスに分けます。OTLP でローカルの Collector に送り、Collector 側のペインに debug エクスポーターで流すのが分かりやすいです。

Collector は以下の設定にしました。

otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
exporters:
  debug:
    verbosity: detailed
service:
  pipelines:
    logs:
      receivers: [otlp]
      exporters: [debug]
    metrics:
      receivers: [otlp]
      exporters: [debug]

ターミナル1で Collector を起動します。このペインにイベントが流れます。実機では otel/opentelemetry-collector 0.154.0 が GRPC:4317 / HTTP:4318 で起動し、Everything is ready まで確認できました。

docker run --rm -p 4317:4317 -p 4318:4318 \
  -v "$(pwd)/otel-collector-config.yaml":/etc/otelcol/config.yaml:ro \
  otel/opentelemetry-collector:latest

ターミナル2で対話セッションを開きます。OTLP 送信に切り替え、本文を出す環境変数を設定します。

export CLAUDE_CODE_ENABLE_TELEMETRY=1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_LOG_USER_PROMPTS=1
export OTEL_LOG_RAW_API_BODIES=1
claude

TUI とログ表示がプロセスごと分かれるので、対話を止めずに user_prompt / api_request_body / tool_result などをリアルタイムに追えます。Grafana などにつなぐ手前の「収集できている」確認ができます。

おわりに

Claude Code の OpenTelemetry 出力を、console エクスポーターだけで手元に出してみました。Collector を立てなくても、プロンプト本文から Messages API のリクエスト/レスポンス本文、ツールの入出力まで順に見えました。file:<dir> を使えば切り詰めなしの全文も残せます。

これらの出力は既定では無効で、OTEL_LOG_TOOL_DETAILSOTEL_LOG_TOOL_CONTENT のように経路で効き方が変わるものもあります。どの粒度で何が残るかは、手元で1つずつ出して確かめるのが早かったです。

監査やログ保全の要件を検討している方は、まず console エクスポーターで会話本文を目視して、残せる粒度を把握してから Collector 構成に移ると無理がないと思います。


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

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

サービス詳細を見る

この記事をシェアする

関連記事