
OpenTelemetry を利用して Claude Code のイベントログを収集してみた
はじめに
Claude Code を利用していると、大量に呼び出される Bash ツールの実行履歴を記録して、あとで確認できるようにしたいと感じる場面があります。
たとえば settings.json で permissions.deny を指定しても、指定が漏れたコマンドがあるかもしれません。
また、私は hooks.PreToolUse を設定してパイプ (|) を含む Bash ツール実行を確認するフローにしていますが、このプロセスが形骸化していて見落としがあるようにも感じています。
Claude Code がユーザの望まない動作をしていないか検証できる監査ログがあれば、permissions.deny の漏れを検証できますし、万が一 Claude Code に対する侵害が発生しても影響範囲を調査する助けとなります。
サプライチェーン攻撃などのセキュリティリスクを考慮すると、監査ログの存在は自己防衛の観点でも重要です。
さて、Claude Code は OpenTelemetry を利用したメトリクス、およびイベントログ出力に対応しています。
今回、OpenTelemetry を利用したパイプラインを用意して、簡単に Claude Code のイベントログを記録できたので、その内容をまとめます。
私自身 OpenTelemetry インフラ構築は初めてなので、OpenTelemetry についてもまとめながら記載します。
OpenTelemetry とは
OpenTelemetry(以降 OTel)は、アプリケーションのオブザーバビリティ(観測可能性)を実現するためのフレームワークとツールキットです。
Traces, Metrics, Logs というアプリケーションに関する 3 つのデータについて、標準的な規格を定めて扱います。
OTel はこれらのデータを統一した形式で収集・転送できるよう、規格・SDK・ツールの参照実装を提供しています。
詳細は公式ドキュメント https://opentelemetry.io/ja/ を参照してください。
OpenTelemetry パイプラインの構成要素
OTel を用いたパイプラインには、次の構成要素が存在します。
Instrumentation
- アプリケーション側に組み込まれ、アプリケーションの動作からログなどのデータを生成する主体です
- 今回は Claude Code に組み込まれた OTel SDK です
Components (Collector)
- SDK から送られたデータを処理するパイプラインの構成要素で、今回は以下の 2 つが関係します
- Receiver: データ受け取り
- Exporter: データ出力
- 今回は Collector (OTel が提供するパイプライン処理プロキシ) に含まれます
Backend
- 最終的にデータを保存・可視化する外部サービスで、Grafana Loki や Prometheus が有名です
- 今回は Backend となるサービスを用意せず、Collector の出力先をファイルにして、それを閲覧・検索することで Backend の代替とします
構成図
今回は次のような構成で Claude Code のイベントログを収集し、監査ログとして機能するか試します。
ホスト (macOS)
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ [Instrumentation] Claude Code セッション群 │
│ │ │
│ │ OTLP/gRPC (localhost:4317) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ [Components] Podman コンテナ (opentelemetry-collector-contrib) │ │
│ │ │ │
│ │ ┌─────────────────`┐ ┌───────────────────────┐ │ │
│ │ │ Receiver │ ──────────────▶ │ Exporter │ │ │
│ │ │ (otlp/grpc:4317) │ │ (file: /data/*.jsonl) │ │ │
│ │ └──────────────────┘ └───────────────────────┘ │ │
│ │ │ │ │
│ │ /data/audit.jsonl │ │
│ └───────────────────────────────────────────────────┼───────────────┘ │
│ │ バインドマウント │
│ [Backend] ~/claude-audit-logs/audit.jsonl │
│ │
└─────────────────────────────────────────────────────────────────────────┘
前提
- macOS (M2 Mac)
- 2026年05月17日時点の Claude Code Docs を参照しています
- Claude Code は
2.1.133を利用しています
実践
Claude Code でログ出力を有効化
Claude Code に必要な環境変数を設定します。全ての Claude Code セッションで有効化するため ~/.claude/settings.json に次の変数を設定します。
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4317",
"OTEL_LOG_TOOL_DETAILS": "1"
}
CLAUDE_CODE_ENABLE_TELEMETRYで OTel によるログ・メトリクス送付を有効化します- Bash ツールのイベント記録目的であれば
OTEL_LOGS_EXPORTERの設定だけで十分ですが、今後メトリクスも確認したいのでOTEL_METRICS_EXPORTERも併せて有効化しています- Exporter として
otlp(OpenTelemetry Protocol) を指定します
- Exporter として
OTEL_EXPORTER_OTLP_PROTOCOLにgrpcを指定するため、後ほど作成する Collector コンテナで4317ポートのフォワーディングが必要です- 今回は Bash ツールの実行履歴を残すため
OTEL_LOG_TOOL_DETAILSを有効化します- これで Bash ツールに渡される引数、つまり実行するコマンドが記録されます
パイプライン準備
Claude Code が出力したログを受け取って記録するパイプラインを準備します。
Collector として opentelemetry-collector-contrib コンテナを準備します。
Collector 用のイメージには次の 2 種類がありますが、今回はファイル出力を行う fileexporter が同梱された opentelemetry-collector-contrib を利用します。
- opentelemetry-collector: 公式イメージ、コア機能のみを含む
- opentelemetry-collector-contrib: コミュニティ拡張イメージ、拡張機能や追加コンポーネントが豊富
設定ファイル準備
Collector の設定を準備します。
まずはコンテナがバインドマウント先となるローカルディレクトリ ~/claude-audit-logs/ の内容です。
$ tree ~/claude-audit-logs/
/Users/xxx/claude-audit-logs/
└── config.yaml
1 directory, 1 file
コンテナ起動後に audit.jsonl が追加されます。
config.yaml には次のものを利用しました。
# データの待受設定
# grpc, http の両方に対応する
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# 受け取ったデータの出力方法
exporters:
file:
path: /data/audit.jsonl
rotation:
max_megabytes: 100 # ログローテーション閾値 (default: 100)
localtime: true
format: json
# 拡張機能設定
# 今回はヘルスチェック拡張を有効化する
extensions:
health_check:
endpoint: 0.0.0.0:13133
# Collector 上で起動するサービスの設定
# これまで設定した機能のうち、起動させるものを指定する
service:
extensions: [health_check]
pipelines:
logs:
receivers: [otlp]
exporters: [file]
metrics:
receivers: [otlp]
exporters: [file]
- ログローテーションについては
.exporters.file.rotationでローテーションの閾値だけ設定し、ローテーション済みのファイルの残存期間は未設定にしています - health_check 拡張を有効にすることで、Collector のステータス確認を有効化します
コンテナ起動
次のコマンドでコンテナを起動します。
この例ではコンテナ管理ツールに Podman を利用していますが、Docker の場合は適宜読み替えてください。
podman run -d \
--name claude-audit-collector \
--restart=always \
-p 127.0.0.1:4317:4317 \
-p 127.0.0.1:4318:4318 \
-p 127.0.0.1:13133:13133 \
-v ~/claude-audit-logs:/data:rw \
ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:latest \
--config=/data/config.yaml
- grpc, http, health_check 用途でポートをフォワーディングします
コンテナの状態は health_check エンドポイントから確認できます。
$ curl -s http://localhost:13133/ | jq .
{
"status": "Server available",
"upSince": "2026-05-17T02:04:48.718180489Z",
"uptime": "2h16m1.596349326s"
}
ログ内容確認
ここまでで Claude 側のログ出力設定と、パイプラインの準備が出来ました。
実際に Claude Code に Bash ツールを実行させて、イベントログを確認します。
次の claude -p コマンドで ls . の実行を依頼します。出力に json を指定することでセッション ID を取得します。
セッション ID がメトリクスとイベントに共通する標準属性で、これをキーとしてログを検索できます。
claude -p "カレントディレクトリのファイル一覧を ls コマンドで取得して答えて" \
--output-format json
Claude Code から回答と、セッション ID などが出力されます。
(~/claude-audit-logs/ 配下で claude -p コマンドを実行しています。)
{
"type": "result",
"subtype": "success",
"is_error": false,
"api_error_status": null,
"duration_ms": 11333,
"duration_api_ms": 5210,
"num_turns": 2,
"result": "カレントディレクトリ `/Users/xxx/claude-audit-logs` には以下の2ファイルがあります。\n\n- `audit.jsonl`\n- `config.yaml`",
"stop_reason": "end_turn",
"session_id": "d618ca8f-7942-4123-9056-29b600b30b18",
... <以下省略>
}
resultに~/claude-audit-logs/配下の 2 つのファイルが回答されています。- セッション ID として
d618ca8f-7942-4123-9056-29b600b30b18が取得できました。
このセッション ID で ~/claude-audit-logs/audit.jsonl を検索します。
ファイルにはログだけでなくメトリクスも含まれるので、今回は tool_use イベントのログのみを検索します。
grep "d618ca8f-7942-4123-9056-29b600b30b18" audit.jsonl \
| grep "tool_use" \
| jq .
結果は次の JSON になります。不要な項目は除去しています。
{
"resourceLogs": [
{
"resource": {
},
"scopeLogs": [
{
"scope": {
"name": "com.anthropic.claude_code.events",
"version": "2.1.133"
},
"logRecords": [
{
"timeUnixNano": "1778992043664000000",
"observedTimeUnixNano": "1778992043664000000",
"body": {
"stringValue": "claude_code.tool_result"
},
"attributes": [
{
"key": "session.id",
"value": {
"stringValue": "d618ca8f-7942-4123-9056-29b600b30b18"
}
},
{
"key": "event.name",
"value": {
"stringValue": "tool_result"
}
},
{
"key": "tool_name",
"value": {
"stringValue": "Bash"
}
},
{
"key": "success",
"value": {
"stringValue": "true"
}
},
{
"key": "tool_parameters",
"value": {
"stringValue": "{\"bash_command\":\"ls\",\"full_command\":\"ls /Users/xxx/claude-audit-logs\",\"description\":\"List files in current directory\"}"
}
},
{
"key": "tool_input",
"value": {
"stringValue": "{\"command\":\"ls /Users/xxx/claude-audit-logs\",\"description\":\"List files in current directory\"}"
}
}
]
}
]
}
]
}
]
}
tool_parametersとtool_inputに、Bash ツールに渡したコマンドが確認できます- Claude Code Docs では
tool_inputに次の記述があるため、監査目的ではtool_parametersを見るのが良さそうです
JSON シリアル化されたツール引数。512 文字を超える個別の値は切り詰められ、全体のペイロードは約 4 K 文字に制限されます。
- Claude Code Docs では
- 今回
claude -pで実行してることもあり、承認ステップを挟まない Bash ツール実行も履歴取得が確認できました
コマンドとともに時刻やセッション ID, 成否も出力されるため「いつ・どのセッションで・どのようなコマンドが呼び出されたか」を検証できます。
まとめ
この記事では OTel に対応したパイプラインをローカル環境に作成して、Claude Code が出力するイベントログを保存する方法を記載しました。
パイプライン内で特に複雑な処理をすることなく気軽に Claude Code のイベントログ記録が可能で、監査ログとして利用できることもわかりました。
今回はローカルファイルへの出力にとどめましたが、Exporter 設定を変えることで Grafana Loki などを利用した可視化にも利用できます。
Claude Code のログやメトリクスを活用することで、安全安心な開発環境づくりにご活用ください。








