OpenTelemetry を利用して Claude Code のイベントログを収集してみた

OpenTelemetry を利用して Claude Code のイベントログを収集してみた

Claude Code のイベントログを OpenTelemetry パイプラインで収集し、監査ログとして機能させる方法を試してみました。ローカル環境での簡単な構築方法から、実際のログ確認まで、実装の流れを紹介します。
2026.05.18

はじめに

Claude Code を利用していると、大量に呼び出される Bash ツールの実行履歴を記録して、あとで確認できるようにしたいと感じる場面があります。
たとえば settings.jsonpermissions.deny を指定しても、指定が漏れたコマンドがあるかもしれません。
また、私は hooks.PreToolUse を設定してパイプ (|) を含む Bash ツール実行を確認するフローにしていますが、このプロセスが形骸化していて見落としがあるようにも感じています。

Claude Code がユーザの望まない動作をしていないか検証できる監査ログがあれば、permissions.deny の漏れを検証できますし、万が一 Claude Code に対する侵害が発生しても影響範囲を調査する助けとなります。
サプライチェーン攻撃などのセキュリティリスクを考慮すると、監査ログの存在は自己防衛の観点でも重要です。

さて、Claude Code は OpenTelemetry を利用したメトリクス、およびイベントログ出力に対応しています。
https://code.claude.com/docs/ja/monitoring-usage

今回、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 つが関係します
    1. Receiver: データ受け取り
    2. 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   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

前提

実践

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) を指定します
  • OTEL_EXPORTER_OTLP_PROTOCOLgrpc を指定するため、後ほど作成する Collector コンテナで 4317 ポートのフォワーディングが必要です
  • 今回は Bash ツールの実行履歴を残すため OTEL_LOG_TOOL_DETAILS を有効化します
    • これで Bash ツールに渡される引数、つまり実行するコマンドが記録されます

パイプライン準備

Claude Code が出力したログを受け取って記録するパイプラインを準備します。
Collector として opentelemetry-collector-contrib コンテナを準備します。
Collector 用のイメージには次の 2 種類がありますが、今回はファイル出力を行う fileexporter が同梱された opentelemetry-collector-contrib を利用します。

  1. opentelemetry-collector: 公式イメージ、コア機能のみを含む
  2. 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_parameterstool_input に、Bash ツールに渡したコマンドが確認できます
    • Claude Code Docs では tool_input に次の記述があるため、監査目的では tool_parameters を見るのが良さそうです

    JSON シリアル化されたツール引数。512 文字を超える個別の値は切り詰められ、全体のペイロードは約 4 K 文字に制限されます。

  • 今回 claude -p で実行してることもあり、承認ステップを挟まない Bash ツール実行も履歴取得が確認できました

コマンドとともに時刻やセッション ID, 成否も出力されるため「いつ・どのセッションで・どのようなコマンドが呼び出されたか」を検証できます。

まとめ

この記事では OTel に対応したパイプラインをローカル環境に作成して、Claude Code が出力するイベントログを保存する方法を記載しました。
パイプライン内で特に複雑な処理をすることなく気軽に Claude Code のイベントログ記録が可能で、監査ログとして利用できることもわかりました。

今回はローカルファイルへの出力にとどめましたが、Exporter 設定を変えることで Grafana Loki などを利用した可視化にも利用できます。
Claude Code のログやメトリクスを活用することで、安全安心な開発環境づくりにご活用ください。


生成AI活用はクラスメソッドにお任せ

過去に支援してきた生成AIの支援実績100+を元にホワイトペーパーを作成しました。御社が抱えている課題のうち、どれが解決できて、どのようなサービスが受けられるのか?4つのフェーズに分けてまとめています。どうぞお気軽にご覧ください。

生成AI資料イメージ

無料でダウンロードする

この記事をシェアする

関連記事