メインコンテンツへスキップ
ブログサイトのロゴsui Tech Blog

Claude Code の利用状況を Cloudflare D1 にためてみる

「どの Skill がどれくらい自動起動しているか」を見るために、Claude Code の 利用状況を Cloudflare Worker + D1 で受け取る仕組みを作りました。個人規模で手軽に始める方法と、Skill 棚卸しに使った実際の集計結果を紹介します。

AI コーディングアシスタントの利用が広がり、Claude Code のように Skill や Plugin でカスタマイズできる環境が整ってきました。カスタマイズの幅が広がるほど、「自分が設定したものが実際に動いているか」が見えにくくなります。

Skill を追加した瞬間は便利そうに思えます。でも、Claude Code が文脈から自律的に Skill を起動しているのか、明示的に指示したときしか動いていないのか、数字で確認したことはありませんでした。

そこで、自分の Claude Code 環境を棚卸しするために、OpenTelemetry 出力を Cloudflare Worker で受け取り、Cloudflare D1 に保存するしくみを作りました。

なぜ利用状況を見たいのか

気になるのは、自分が明示的に呼び出した回数ではありません。Claude Code が文脈から判断して、どれくらい自律的に Skill を起動しているかです。

たとえば Git のコミットメッセージをある程度そろえたいとき、毎回「この形式で書いてください」と指示するより Skill に寄せた方が楽です。Claude Code が必要なタイミングで自動的に起動してくれれば、私が意識しなくてもよくなります。

「便利なはず」と思って作った Skill でも、description が明確でないと、必要な場面でも Claude Code がその Skill を見つけられないかもしれません。明示的に呼び出さなくても自然に使われているかどうかは、数字を見るまで分かりません。

Skill だけでなく、Plugin、API リクエスト、トークン使用量、セッション数も同じです。感覚で「使えている気がする」という状態をやめて、まず数字で見られるようにしたいと思いました。

今回やること

Cloudflare Worker に OTLP の /v1/logs/v1/metrics を用意し、Claude Code から送られてくるログとメトリクスを D1 に保存します。

Claude Code は、OpenTelemetry の logs/events と metrics をエクスポートできます。公式ドキュメントを見ると、Skill の起動を表す claude_code.skill_activated だけでなく、Plugin の読み込み、API リクエスト、コスト、トークン使用量、セッション数なども出力できます。

作った Worker では、ログ系のイベントを /v1/logs で受け取り、メトリクス系のデータを /v1/metrics で受け取ります。保存先は D1 です。

Worker がリクエストを受け取り、必要な値を取り出して D1 に入れておけば、SQL で雑に集計できます。

全体構成

Cloudflare Workers Cloudflare D1 OTLP http/jsonAuthorization: Bearer /v1/logs /v1/metrics Claude CodeOTEL を有効化 Cloudflare Worker/v1/logs・/v1/metrics Bearer 認証 OTLP JSON を検証 エンドポイントで分岐 イベントとして整形 メトリクスとして整形 skill_eventsplugin_eventsapi_requests など cost_usagetoken_usagesession_counts など

Cloudflare Workers の対応プロトコルを見ると、基本は HTTP / HTTPS の fetch() ハンドラでリクエストを受ける形です。そのため、OTLP は grpc ではなく http/json を使いました。

OTLP の JSON は入れ子構造になっているため、Worker 側で必要な値を取り出して D1 に保存します。公式の JSON サンプルとしては opentelemetry-proto の logs.json がありますが、この記事では実装側の処理に話を絞ります。

Claude Code から OTEL を送る設定

Claude Code 側では、~/.claude/settings.jsonenv を設定します。私の用途では、以下のような設定にしました。

{
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_LOG_TOOL_DETAILS": "1",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_METRICS_INCLUDE_VERSION": "true",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/json",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "https://cc-monitor-worker.<account>.workers.dev",
    "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer <登録したトークン>"
  }
}

CLAUDE_CODE_ENABLE_TELEMETRY=1 は必須です。OTEL_LOGS_EXPORTER=otlp を指定するとイベントが送信され、OTEL_METRICS_EXPORTER=otlp を指定するとメトリクスも送信されます。

ここで重要なのは OTEL_LOG_TOOL_DETAILS=1 です。これがないと、ユーザー定義 Skill やサードパーティ由来の Skill 名が custom_skill のように匿名化される場合があります。自分の Skill の棚卸しをしたいので、ここは有効にしました。

OTEL_LOG_USER_PROMPTS を有効にするとユーザーが入力したプロンプトの内容が、OTEL_LOG_TOOL_CONTENT を有効にするとツール実行時の入出力が OTEL に含まれます。どちらも有効にしていません。利用状況の集計が目的ですので、プロンプト本文やツールの入出力は必要ないですし、D1 のレコードに機密情報が混入するリスクも避けたいです。

D1 に保存するデータ

詳しい実装は Suntory-N-Water/cc-monitor-worker にありますので、興味ある人は見てみてください!Cloudflare Workers のデプロイ先を変更するだけで動作するしくみにはなっています。

最初は Skill の起動ログだけを考えていましたが、Claude Code の利用状況を見るなら、コストやトークン使用量も同じ場所に保存した方が見やすいです。現在は、以下のようなテーブルに分けています。

テーブル 保存するもの
skill_events Skill の起動ログ
plugin_events Plugin のロード・インストール履歴
api_requests API リクエストのモデル、コスト、トークン数
tool_results ツール実行結果
hook_executions Hook の実行結果
cost_usage コスト使用量
token_usage トークン使用量
session_counts セッション数
active_time アクティブ時間

普段見たいのは Skill 名、起動方法、Plugin 名、コスト、トークン使用量あたりです。プロンプト本文やツールの出力内容は見たい対象ではないので、Claude Code 側の設定でも送らないようにしています。

Worker がリクエストを受け取って D1 に入れる

Worker 側は Hono で実装しています。全文は載せませんが、実際のフローはこのような感じです。

  1. /v1/* のリクエストで Authorization ヘッダを確認する
  2. /v1/logs/v1/metrics にルーティングする
  3. OTLP の JSON ペイロードを検証する
  4. /v1/logs では skill_activatedplugin_loadedapi_request などのイベントごとにレコードを作成
  5. /v1/metrics では claude_code.cost.usageclaude_code.token.usageclaude_code.session.count などのメトリクスごとにレコードを作成
  6. D1 に保存する
  7. Claude Code には { partialSuccess: {} } を返す

Cloudflare Worker で受け取る場合は、OTLP の JSON を自分でたどって、必要な属性を取り出して、D1 のテーブルに入れる必要があります。ここは BigQuery や専用の OTEL バックエンドに送る場合とは異なります。

この Worker の目的は、きれいなトレース基盤を作ることではありません。Claude Code の利用状況をあとから見られるようにすることです。Worker 側も、まずはログとメトリクスの受信、検証、保存に絞りました。

実際に D1 を見てみる

skill_events テーブルには起動のきっかけを示す invocation_trigger カラムがあります。claude-proactive はユーザーが指示しなくても、Claude Code が文脈から判断して自律的に起動したケースです。ここを絞り込むと、自律的に動いている Skill だけを見られます。

SELECT
  skill_name,
  COUNT(*) AS cnt
FROM skill_events
WHERE invocation_trigger = 'claude-proactive'
GROUP BY skill_name
ORDER BY cnt DESC;
skill_name 件数
general-dev-skills:managing-Git-GitHub-workflow 5
wrangler 4
notebooklm 3
managing-Git-GitHub-workflow 2
agent-browser 1
article-japanese-composition-review 1
article-structure-review 1
general-dev-skills:actions-check 1
hono 1
init 1
modern-web-guidance 1
playwright-best-practices 1

GitHub 関連の Skill が最多でした。コミットや Actions の確認は毎日発生するので、ここが多いのは自然です。wranglernotebooklm も複数回自律起動していたので、description はうまく機能していると言えます。

一方、playwright-best-practices は 1 回です。用途が限定的な Skill ですので、必要な場面で 1 回でも自律起動していれば十分です。問題にしたいのは、この結果に出てこない Skill です。テーブルに現れない Skill は、明示的に呼び出してしか使われていないか、そもそもほぼ起動されていないかのどちらかです。

やってみて

まだログを取り始めた段階ですので、「なんとなく使っている気がする」で放置せず、数字で見直す土台は作れたと思います。

社員に何もさせずにClaude Code利用ログを集める ── 数百名規模のOpenTelemetry収集基盤の構築では、数百名規模の Claude Code 利用ログを Google Cloud と BigQuery で扱っています。組織で本格的に使うなら、既存の分析基盤に乗せる方がよいと思います。今回はそこまで大きな話ではなく、個人でまず試すために Cloudflare Worker と D1 を使いました。

もう少しデータがたまったら、Skill の description を直した前後で自動起動が増えるのか、Plugin ごとのコストやトークン使用量に偏りがあるのかも見てみたいです。

まとめ

  • CLAUDE_CODE_ENABLE_TELEMETRY=1 と各エクスポーター設定を settings.json に追加するだけで利用状況の送信をできる
  • Cloudflare Worker で OTLP http/json のリクエストを受け取り、D1 に保存するとあとから SQL で集計できる
  • invocation_trigger = 'claude-proactive' で絞り込むと、Claude Code が自律的に起動した Skill だけを見られる。この結果に出てこない Skill は description の見直し候補になる
  • OTEL_LOG_TOOL_DETAILS=1 がないと Skill 名が匿名化されてしまうため、詳細な利用状況を見たい場合は有効にする

参考

code.claude.com のアイコン
code.claude.com

Monitoring - Claude Code Docs

Learn how to enable and configure OpenTelemetry for Claude Code.

code.claude.com のアイコン
code.claude.com

Environment variables - Claude Code Docs

Reference for environment variables that control Claude Code behavior.

developers.cloudflare.com のアイコン
developers.cloudflare.com

Protocols

Supported protocols on the Workers platform.

developers.cloudflare.com のアイコン
developers.cloudflare.com

Cloudflare D1

D1 is Cloudflare's managed, serverless database with SQLite's SQL semantics, built-in disaster recovery, and Worker and HTTP API access.

github.com のアイコン
github.com

GitHub - Suntory-N-Water/cc-monitor-worker: Cloudflare Workersを活用したClaude Codeのモニタリング

Cloudflare Workersを活用したClaude Codeのモニタリング. Contribute to Suntory-N-Water/cc-monitor-worker development by creating an account on GitHub.

techblog.zozo.com のアイコン
techblog.zozo.com

社員に何もさせずにClaude Code利用ログを集める ── 数百名規模のOpenTelemetry収集基盤の構築 - ZOZO TECH BLOG

こんにちは、技術戦略部CTOブロックの塩崎です。 当社ZOZOには1人あたり月額200ドルの基準のもと、Claude CodeやGemini CLIをはじめとした各種AI開発ツールを利用可能にする制度を2025年7月にスタートさせました。 corp.zozo.com 現在ではこの制度を用いて数百名という非常に多くの社員がClaude Codeを利用しています。このような中で組織全体のAI活用を推進するためには、それぞれの社員や部署のClaude Codeの利用状況をモニタリングすることが重要です。そのためにClaude CodeのOpenTelemetry機能を利用して、全社員のClaude …

理解度チェック

問題1: `skill_events` テーブルを `invocation_trigger = 'claude-proactive'` でフィルタリングすると、何が分かりますか?

  • Claude Code が文脈から判断して自律的に起動した Skill の一覧

    正解正解です!

    `claude-proactive` はユーザーが指示しなくても、Claude Code が文脈から判断して起動したケースに対応します。この結果に出てこない Skill は description の見直し候補になります。

  • ユーザーが明示的に「この Skill を使って」と指示した回数

    不正解もう一度考えてみましょう!

    `claude-proactive` はその逆で、ユーザーが指示しなくても自律起動したケースです。

  • Skill の合計起動回数

    不正解もう一度考えてみましょう!

  • Plugin のロード回数

    不正解もう一度考えてみましょう!

問題2: `OTEL_LOG_TOOL_DETAILS=1` を設定しなかった場合、どのような影響がありますか?

  • ユーザー定義 Skill やサードパーティ由来の Skill 名が `custom_skill` のように匿名化される

    正解正解です!

    Skill の棚卸しには具体的な Skill 名が必要なため、自律起動の分析をしたい場合はこの設定が必須です。

  • OTEL のデータが送信されなくなる

    不正解もう一度考えてみましょう!

    データ自体は送信されます。Skill 名が匿名化されるだけです。

  • プロンプトの内容が OTEL に含まれてしまう

    不正解もう一度考えてみましょう!

  • D1 への保存が失敗する

    不正解もう一度考えてみましょう!

関連記事