1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeの全ログをClickHouseで分析したら、6月だけでAPI換算56万円使っていた

1
Posted at

「AI時代のデータベース」をいったん文字通りに受け取ってみる

このテーマでよく語られるのはベクトル検索とRAGです。しかし、今回はAIエージェント自身が吐き続けているログに注目してみることにしました。

Claude Codeを使っている人なら、~/.claude/projects/ の下を覗いてみてください。全セッションの生ログがJSONLで丸ごと残っています。

1行が1イベント(ユーザー発話・アシスタント応答・ツール呼び出し)で、タイムスタンプ、モデル名、入出力トークン数、キャッシュヒットまで全部入りです。

私の環境では約2ヶ月分で112ファイル・289MBになっていました(が、このPCにあるやつなので、本当はもっと使ってると思います。)

これの構造はまさにOLAPのワークロードです。追記オンリー、時系列、集計してなんぼ、つまり「AI時代のデータベース」で最初にやってみたいのは、AI自身が吐くテレメトリなのでは?

というわけで、お試しがてらccsql というのを作りました。Claude CodeのセッションログをClickHouseに取り込んで、コスト・ツール利用・キャッシュ効率をSQLで深堀するためのCLIです。

リポジトリ: https://github.com/Subara3/ccsql

$ pip install ccsql        # 埋め込みClickHouse(chdb)ごと入ります
$ ccsql load
files=112 rows=36070 skipped=19883
loaded into chdb (~/.ccsql)

$ ccsql report cost
┌───────date─┬─model───────────┬─calls─┬─in_tok─┬─out_tok─┬─────usd─┬───jpy─┐
│ 2026-07-08 │ claude-fable-5  │   486 │ 161334 │  760349 │ 164.652 │ 25521 │
│ 2026-07-08 │ claude-sonnet-5 │   333 │  23964 │  348731 │  33.469 │  5188 │
...

この記事に載せる出力は、断りがない限りすべて私の実ログを取り込んだ実行結果です。

偉大な先駆者として、Claude Codeが始まったときに生まれたccusage(npm)がありますよね。こちらにも大変お世話になっております。

ccsqlは自由SQLの口が開いているのが違いで、「水曜のOpus利用が突出してるのはなぜ?」「このプロジェクトだけキャッシュヒット率が低いのは?」みたいな、次の疑問にその場でSQLで答えられます。分析の主導権が定型レポートからこちらに戻ってくる感覚です。

なぜClickHouseか。そして、なぜサーバーを立てないのか

手元は chdb(埋め込みClickHouse)。 ClickHouseにはchdbという埋め込み版があって、pip install chdb だけでプロセス内にClickHouseエンジンが立ちます。サーバーなし、Dockerなし、設定ファイルなし。SQLiteの感覚でClickHouseのSQL特有のもの(ARRAY JOINLowCardinalityも)が全部使えます。

この記事を再現するのに要る手順は pip install 1回です。やったね!!

ただし、chdbのwheelはLinuxとmacOS向けしかありません。 Windowsネイティブの pip install chdb は解決不能エラーで落ちます。私はWindows機なので、この記事の計測はすべてWSL2(Ubuntu 22.04)上で行いました。ログは /mnt/c/Users/<名前>/.claude/projects でWindows側のものがそのまま読めるので、実用上は --dir を1回指定するだけです。

ClickHouse Cloud(無料トライアル)を使うこともできます。 ccsqlは --cloud オプションで接続先をClickHouse Cloudに切り替えられます。
同じスキーマ・同じレポートがそのまま動いてくれるわけです。

チームで各メンバーのログを1箇所に集めて「チームのAI利用ダッシュボード」にする、という発展はCloud側の話になるので、後半で触れます。

JSONLの生ログは1行に会話本文まで入っていて重い(だから289MBある)のですが、分析で触るのはトークン数・タイムスタンプ・モデル名みたいな数カラムだけです。

行指向だと毎回全部読むところ、列指向は要る列しか読まない。しかもモデル名やプロジェクト名は種類が少ないのでLowCardinality(String)が辞書圧縮でいい感じになってくれるはずです。

データの形と、防御的パース

~/.claude/projects/<プロジェクト>/<セッションID>.jsonl の1行はだいたいこうです(実物から抜粋・整形)。

{
  "type": "assistant",
  "sessionId": "de55b4eb-...",
  "timestamp": "2026-06-17T16:01:08Z",
  "version": "2.0.14",
  "message": {
    "model": "claude-opus-4-8",
    "content": [
      { "type": "text", "text": "..." },
      { "type": "tool_use", "id": "toolu_...", "name": "Edit", "input": {...} }
    ],
    "usage": {
      "input_tokens": 4200,
      "output_tokens": 890,
      "cache_read_input_tokens": 31000,
      "cache_creation_input_tokens": 0
    }
  }
}

注意点として、この形式はClaude Codeの内部仕様で、公式に文書化されたスキーマではありません。バージョンで変わりえます。なのでccsqlのパーサは全フィールドを.get()で拾い、壊れた行はスキップして件数だけ報告する防御的な作りにしました。

このスキップを実際に走らせると skipped=19883——全体の3分の1以上が捨てられます。中身を数えてみると ai-titlepermission-modefile-history-snapshotqueue-operation など、会話ではないUI状態やスナップショットの行がこれだけ混ざっていました。
「知らないtypeは黙って捨てて件数だけ出す」方針でやっています。

取り込み後のテーブルはこの13カラムです。

CREATE TABLE cc_events (
  ts DateTime64(3),
  date Date,
  project LowCardinality(String),
  session_id String,
  event_type LowCardinality(String),
  model LowCardinality(String),
  input_tokens UInt64,
  output_tokens UInt64,
  cache_read_tokens UInt64,
  cache_creation_tokens UInt64,
  tool_calls Array(LowCardinality(String)),
  text_chars UInt32,
  cc_version LowCardinality(String)
) ENGINE = MergeTree ORDER BY (project, ts)

tool_callsを配列で持っているのがポイントで、後述のARRAY JOINが働きます。

取り込みは289MB・112ファイルで約24秒でした(WSL2、ボトルネックはJSONパースでchdb側ではない)。

何が見えるか:5つの定型レポート

report cost — 昨日いくら使った?

日次×モデルでトークン数とコスト概算(USD/JPY)を出します。
*Sonnet 5は導入価格前の通常単価で計算しています。

$ python3 ccsql.py report cost
# 開発中のリポジトリで直叩きしています。
┌───────date─┬─model───────────┬─calls─┬──in_tok─┬─out_tok─┬─────usd─┬────jpy─┐
│ 2026-07-08 │ claude-fable-5  │   486 │  161334 │  760349 │ 164.652 │  25521 │
│ 2026-07-08 │ claude-sonnet-5 │   333 │   23964 │  348731 │  33.469 │   5188 │
│ 2026-07-06 │ claude-fable-5  │   836 │  230354 │ 1420053 │ 334.585 │  51861 │
│ 2026-07-05 │ claude-sonnet-5 │  3914 │  111949 │ 2777601 │ 604.786 │  93742 │
│ 2026-07-05 │ claude-fable-5  │   838 │  378459 │ 1293295 │ 271.227 │  42040 │
│ 2026-06-30 │ claude-opus-4-8 │  2527 │  974320 │ 4779688 │ 782.133 │ 121231 │
...

image.png

※実際のターミナル。

単価はコード先頭の辞書で持っていて、キャッシュ読み取りは入力単価の0.1倍、キャッシュ書き込みは1.25倍で概算しています。単価は変わるものなので、使う前に必ず公式の料金ページと突き合わせてくださいね。

また、このusdAPI従量課金換算の値です。私はサブスクリプション(定額)で使っているので実請求ではありません。私は6月に$3,494(約54万円)相当のトークンを使ったようです。

Fableを使い倒してるからね!!

report tools — Claude Codeは何をしているのか

$ python3 ccsql.py report tools
┌─tool────────────────────────────┬─calls─┬─sessions─┬─calls_per_day─┐
│ Bash                            │  3320 │       46 │          92.2 │
│ Edit                            │  2415 │       43 │            69 │
│ Read                            │  2335 │       50 │          64.9 │
│ PowerShell                      │   615 │       32 │          21.2 │
│ Write                           │   590 │       42 │          17.9 │
│ Grep                            │   589 │       27 │            21 │
│ mcp__claude-in-chrome__computer │   174 │        5 │            58 │
│ TaskUpdate                      │   114 │        7 │            19 │
│ AskUserQuestion                 │   112 │       34 │           3.6 │
│ WebSearch                       │   105 │       15 │           6.2 │
...

中身はARRAY JOIN tool_callsで配列を展開して数えているだけですが、自分のエージェントが「読む」「書く」「実行する」をどんな比率でやっているかが初めて数字になります。私のログだと実行系(Bash+PowerShell)が約4,000回で最多。Windows民なのでPowerShellが615回混ざっているのと、ブラウザ自動化MCPが174回いるのが個人の癖でしょうかね。

AskUserQuestionが112回です。

report cache — 節約できているかの答え合わせ

cache_read / (input + cache_read) をプロジェクト別に出します。プロンプトキャッシュはClaude Codeのコスト構造の生命線なのに、効いているかどうかを普段は確かめようがないわけですね。

$ python3 ccsql.py report cache
┌─project───────────────────┬─fresh_in─┬─cache_read─┬─cache_write─┬─cache_hit_pct─┐
│ project-A                 │    83144 │ 1782260468 │    25113388 │           100 │
│ project-B                 │   239120 │  819888931 │    16954305 │           100 │
│ project-C                 │  1700012 │ 2180541633 │    61364983 │          99.9 │
...
│ D---------Qiita2026summer │    20367 │     810254 │       54307 │          97.5 │
└───────────────────────────┴──────────┴────────────┴─────────────┴───────────────┘

実データを見て予想が外れたのがここで、事前には「キャッシュが効いてないプロジェクトを見つけて改善する」つもりだったのに、**どこも99〜100%**でした(分母にcache_creationを入れても98%くらい)。Claude Codeのキャッシュ戦略は相当優秀です。代わりに見えたのは逆方向の法則で、ヒット率が一番低い(97.5%)のはこの記事を書き始めたばかりのプロジェクトでした。

キャッシュは会話が積み上がるほど働いてくれるはずなので、ヒット率はプロジェクトの「会話の蓄積量」のプロキシになっていそうですね。cache_readの絶対量に注目すると、最大のプロジェクトは21億トークンをキャッシュから読んでいます。全部通常入力単価で払っていたらと思うと、やはりこの単価がいつまで続くものか、悩ましいですね。

report sessions — 一番重かった作業はどれか

コストの重いセッションTop20です。

$ python3 ccsql.py report sessions
┌─session_id─┬─project───┬─────────────────started─┬─minutes─┬─user_turns─┬─tool_calls─┬─────usd─┐
│ edc0531e-… │ project-D │ 2026-07-04 21:33:42.736 │    2435 │       2423 │       2233 │ 865.703 │
│ 1e89c0fa-… │ project-C │ 2026-06-24 21:41:40.742 │    7814 │        867 │        675 │ 554.039 │
│ 93f92a7c-… │ project-C │ 2026-06-30 04:17:58.987 │    2409 │        777 │        645 │ 488.187 │
...

トップは1セッションでAPI換算 $865、ツール呼び出し2,233回。2位は開始から 7,814分(5.4日) 続いたセッションです(--resumeで継ぎ足した結果ですが、ログ上は1つのセッション)。

「あの週末の大改修、いくら分だったんだろう」というのが分かるようになりました。

report heatmap — 自分はいつAIと働いているのか

曜日×時間帯の利用分布をターミナルにそのまま描画します。私の実データがこれです。

$ python3 ccsql.py report heatmap
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3   ← 時間帯(0-23時)
  月 ▂ ▂ ▂ ▃ ▂ ▁                               ▁ ▁ ▄   4068
  火       ▁ ▂ ▁ ▂ ▃ ▂                     ▂ ▁ ▃ ▃ ▅   5798
  水 ▅             ▂ ▁                     ▃ ▃ ▄ ▁     4287
  木   ▁ ▁                                 ▁ ▁ ▂ ▁ ▁   1491
  金 ▁ ▂ ▁ ▁                                           1168
  土   ▁                     ▁ ▁   ▁ ▁ ▁ ▁ ▁ ▂ ▂ ▁ ▂   3505
  日 ▂ ▃ █ █ ▅ ▂ ▁ ▁     ▁   ▁ ▁ ▄ ▇ ▅ ▃ ▃ ▃ ▃ ▅ ▂ ▁   15753

  ピーク: 1863 events/時

日曜だけで15,753イベントで全体の4割、しかもピークは日曜の深夜2〜3時。逆に金曜はほぼ灯りが消えている。リベンジ夜更かしを極めている。数字で見ると思っていたより極端でした。自分の働き方の答え合わせにどうぞ。

そして自由SQL

テーブルはcc_eventsひとつ、ClickHouseのSQLがそのまま使えます。たとえば「月ごとのAPI換算コスト、モデル世代の単価込みで」:

$ python3 ccsql.py sql "
  SELECT toStartOfMonth(date) AS month,
         round(sum(multiIf(
           startsWith(model,'claude-fable-5'),  (input_tokens*10 + cache_read_tokens*1.0  + cache_creation_tokens*12.5 + output_tokens*50)/1e6,
           startsWith(model,'claude-opus-4-8'), (input_tokens*5  + cache_read_tokens*0.5  + cache_creation_tokens*6.25 + output_tokens*25)/1e6,
           startsWith(model,'claude-sonnet'),   (input_tokens*3  + cache_read_tokens*0.3  + cache_creation_tokens*3.75 + output_tokens*15)/1e6,
           startsWith(model,'claude-haiku'),    (input_tokens*1  + cache_read_tokens*0.1  + cache_creation_tokens*1.25 + output_tokens*5)/1e6,
           0)), 0) AS usd
  FROM cc_events WHERE event_type = 'assistant'
  GROUP BY month ORDER BY month"

   ┌──────month─┬──usd─┐
1. │ 2026-05-01 │    8 │
2. │ 2026-06-01 │ 3494 │
3. │ 2026-07-01 │ 1915 │
   └────────────┴──────┘

6月$3,494、7月は8日時点で$1,915。ペースが上がっているのはFable 5に乗り換えたからです。SQL1本で答えが返ってくるのがこのツールを作った意味というものでしょう!

爆速の看板に偽りはないか:500万行で実測

手元の実ログは3.6万行ですが、チーム全員分を1年集めたらどうなるんだろう、というのを想定して、同じスキーマの合成データ500万行で集計速度を測りました。環境はWSL2(Ubuntu 22.04、20スレッド、メモリ11GB割り当て)です。

500万行の挿入(JSONEachRow, 10万行×50バッチ): 89.7s
日次×モデル集計(GROUP BY 2キー + 集計4本 + コスト計算): 122〜208ms(3回実行)
ディスク上のサイズ: 162.84 MiB

500万行が163MB、集計はサブ0.2秒。インデックスのチューニングは一切していなくて、ORDER BY (project, ts)を宣言しただけです。合成データは乱数で圧縮に不利な条件なので、実ログならもっと縮むはずだと思います。
この「雑に投げてもサブ秒」がClickHouseの体験の中心ですね。

ClickHouse Cloud無料トライアルに載せる

では何のためにCloudか。ログが自分のPCの外で生まれ始めたときです。CIで動くClaude Code、チームメンバーのログ、複数マシン。手元のchdbはファイルが手元にある前提なので、集約するなら中央のClickHouseが要ります。

ccsqlは環境変数とオプション1つでCloudに向きます。

pip install 'ccsql[cloud]'   # clickhouse-connectが追加で入る
export CLICKHOUSE_HOST=<トライアルのホスト>.clickhouse.cloud
export CLICKHOUSE_PASSWORD=<パスワード>
python3 ccsql.py --cloud load
python3 ccsql.py --cloud report cost

無料トライアルはClickHouse公式サイトから開始できて、サービス作成後に表示される接続情報をそのまま入れるだけです。実際に無料トライアル(GCP東京リージョン)を立てて試しました。会話ログをクラウドに上げる話なので、実測はfakeログ生成スクリプトで作った合成ログ31ファイル・7,759行で行っています。

$ python3 ccsql.py --cloud load --dir ~/fake_claude/projects
files=31 rows=7759 skipped=0
loaded into ClickHouse Cloud      # 2.8秒
$ python3 ccsql.py --cloud report cost     # ← chdbと同じコマンドがそのまま通る
$ python3 ccsql.py --cloud report heatmap  # ← これも

DDLもレポートSQLもchdbと完全に同一のものが動きました。ここが「同じエンジンの埋め込み版と本体」という関係の良さで、SQLiteとPostgreSQLのような方言の壁がありません。chdbで書いた分析資産が、ログがローカルを卒業した日にそのまま使えます。

image.png

一個苦戦したのが、chdbへの取り込みはJSONEachRowなので日付が文字列のままでも通るのですが、Cloud接続に使うclickhouse-connectのネイティブinsertはDate/DateTimeカラムにPythonのdatetime型そのものを要求します。同じエンジンでも、クライアントライブラリの流儀は別物。ccsqlはCloud側のinsertで型変換を挟んで吸収しています。

ハマりどころ

作る過程で実際に悩んだ点です。

  1. chdbはWindowsネイティブでは動かない。 wheelがLinux/macOSのみで、pip install chdbが依存解決エラーで落ちます。WSLなら問題なし。Windows側のログは/mnt/c/...で見えるので実害は--dir指定1回分です。
  2. タイムスタンプはUTC。 "2026-06-17T16:01:08Z"を素直に入れると、ヒートマップが9時間ずれます。最初の版で「土曜の夕方がピーク」と出て、実際は日曜の深夜2時でした。ccsqlは取り込み時にPython側でローカル時刻へ変換しています。時系列ログあるあるですが、可視化して初めて気づくタイプのバグです。
  3. chdbの永続化はSession。 chdb.query()単発はインメモリで消えます。chdb.session.Session(path)でパスを渡すと、そのディレクトリにMergeTreeの実体が残り、次回起動でもテーブルが生きています。
  4. INSERTはJSONEachRowが楽。 Pythonのdictをjson.dumpsで1行ずつ連結してINSERT INTO ... FORMAT JSONEachRowに流すと、配列カラム(tool_calls)も型変換も勝手に面倒を見てくれます。VALUES句の組み立てより事故が少ない。
  5. ログのスキーマは信用しない。 前述の通り実ログの3分の1以上(19,883行)が会話以外の行でした。skipped件数を必ず出す設計にしておくと、バージョン違いで欠けるフィールドがあっても集計は走るし、捨てすぎていればすぐ気づけます。モデル名に<synthetic>という内部行が混ざるのもレポート側で除外しています。
  6. 単価はコードに埋めない…のが理想だが。 今回は辞書で持ちました。モデル追加・改定のたびに書き換える前提で、コード先頭に置いて目立たせています。旧単価のまま計算して3倍の数字を出しかけた実績(前述)があるので、これは本当に注意。
  7. 秘密情報。 ログには会話本文が含まれます。ccsqlは本文を文字数(text_chars)に落として本文自体は取り込まない設計にしました。Cloudに上げるときにここが効きます。

まとめ

  • Claude Codeは~/.claude/projects/に全セッションをJSONLで残している(私の場合2ヶ月で289MB)。これは分析されるのを待っているOLAPデータ
  • chdb(埋め込みClickHouse)ならpip 1回でこのログにSQLが打てる。500万行の集計が162MB・サブ0.2秒。ただしWindowsはWSL経由
  • コスト・ツール比率・キャッシュヒット率が数字になると、発見が具体的になる。私の場合:6月のAPI換算$3,494、キャッシュ読み取り累計21億トークン、ピークは日曜深夜2時
  • ログが手元を離れたらClickHouse Cloud。同じSQLがそのまま動くので、chdbで書いた資産が無駄にならない

「AI時代のデータベース」は、AIのためのデータを入れる箱であると同時に、AIが吐き出したデータを人間が理解するための道具でもあるわけですね。そしていくら得したのか数えるのは楽しい。Anthropicは果たしてこの値段でAIを提供し続けてくれるのか。これはボーナスタイムなのか。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?