← Cookbook トップへ

Claude Code の Token 消費をプロジェクト別に Recorder に蓄積する (Stop hook)

Stop hook が Claude Code セッション終了時に JSONL transcript を集計して、Input / Output / CacheRead / CacheCreate の 4 種を Recorder に POST するパターン。Claude Code の起動 dir を 1 つの metric prefix に紐付ける設計なので、`System/Code/<app>/Claude/Tokens` に揃えれば自分の DORA メトリクスと同じツリーに並べられます。Anthropic Console では取れないアプリ別 / dir 別のClaude コスト時系列が手に入ります。

このレシピで取得・記録できるもの

Claude Code の Stop hook で、session 終了時に JSONL transcript を集計して Recorder に POST するパターンです。session ごとに 4 つの値を任意の <MyPrefix> 配下に記録します:

  • <MyPrefix>/Input — 入力 token (素の prompt)
  • <MyPrefix>/Output — 出力 token
  • <MyPrefix>/CacheRead — prompt cache から再利用された token
  • <MyPrefix>/CacheCreate — prompt cache に書き込んだ token

<MyPrefix>Claude Code を起動する dir 1 つに対して 1 つ命名 します。System/Code/<app>/Claude/Tokens の形に揃えておくと、DORA レシピ で蓄積する Lines や Deploy 回数と同じツリーに並び、Recorder 上で「LOC 増加 vs Token 消費」「Deploy 回数 vs Token 消費」を 1 画面に重ねられます。これは Anthropic Console (API key 単位の Usage) では出せない切り口です。

前提

  • Claude Code がローカルにインストール済み
  • Recorder のアカウント
  • jq (集計に使用)

なぜ hook で送るのか

Claude Code のセッション transcript は ローカルにしか存在しません (~/.claude/projects/<encoded-cwd>/<session-id>.jsonl)。各行に message.usage = {input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens} がタイムスタンプ付きで残っているので、ローカルから直接読んで Recorder に流すのが自然です。GitHub Actions の cron では到達できないデータ源です。

セットアップ手順

1. Recorder の PAT を発行

Settings → API Tokens で発行 → 文字列をその場でコピー → ~/.zshrc 等で export RECORDER_PAT="..." しておきます。

2. 集計対象の dir を事前確認する

Claude Code は launch cwd を /- で置換して project dir を作ります。集計したい dir で 1 回 Claude Code を起動すると、~/.claude/projects/ の下に対応する dir が作られます。ls して basename を控えます:

ls ~/.claude/projects/
# 例: -Users-me-code-myapps   ← この行を控える

3. Stop hook script を配置し、対象 dir と metric prefix を反映する

~/bin/post_claude_tokens.sh を作成。case の 2 行だけ自分の値に書き換えれば、残りはそのままで動きます。

#!/usr/bin/env bash
set -euo pipefail
 
: "${RECORDER_PAT:?env RECORDER_PAT is not set}"
 
PAYLOAD=$(cat)
TRANSCRIPT=$(echo "$PAYLOAD" | jq -r '.transcript_path')
PROJECT_BASENAME=$(basename "$(dirname "$TRANSCRIPT")")
 
# ─── ⬇ ここの 2 行だけ自分の環境に書き換える ⬇ ───
case "$PROJECT_BASENAME" in
  -Users-me-code-myapps)                                # ← 手順 2 で確認した dir basename
    METRIC_PREFIX="System/Code/myapps/Claude/Tokens" ;; # Recorder 上で使う prefix
  *)
    exit 0 ;;
esac
# ─── ⬆ ここまで ⬆ ───
 
read -r INPUT OUTPUT CREAD CCREATE <<<"$(jq -s -r '
  reduce .[] as $e (
    {input:0, output:0, cread:0, ccreate:0};
    .input    += ($e.message.usage.input_tokens // 0)
    | .output   += ($e.message.usage.output_tokens // 0)
    | .cread    += ($e.message.usage.cache_read_input_tokens // 0)
    | .ccreate  += ($e.message.usage.cache_creation_input_tokens // 0)
  ) | "\(.input) \(.output) \(.cread) \(.ccreate)"
' "$TRANSCRIPT")"
 
post() {
  local name=$1 value=$2
  curl -sS -X POST https://api.recorder.nasebanal.com/api/v1/records \
    -H "Authorization: Bearer $RECORDER_PAT" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"$name\",\"unit\":\"tokens\",\"data_type\":\"integer\",\"chart_type\":\"line\",\"value\":$value}"
}
 
post "$METRIC_PREFIX/Input"       "$INPUT"
post "$METRIC_PREFIX/Output"      "$OUTPUT"
post "$METRIC_PREFIX/CacheRead"   "$CREAD"
post "$METRIC_PREFIX/CacheCreate" "$CCREATE"

実行権限を付けます:

chmod +x ~/bin/post_claude_tokens.sh

集計対象 dir を複数にしたい場合は case 文に arm を追加するだけです。

4. Claude Code の settings に Stop hook を登録

~/.claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/bin/post_claude_tokens.sh"
          }
        ]
      }
    ]
  }
}

5. 動作確認

手順 2 で控えた dir で 1 session 動かして終了させ、Recorder の「タグ一覧」に <MyPrefix>/{Input,Output,CacheRead,CacheCreate} が現れていれば成功です。

⚠️ シークレットの取扱い

  • RECORDER_PAT~/.zshrc 等で export するだけにして、script に直書きしないでください
  • 漏洩が疑われたら Recorder Settings → API Tokens で失効 → 新規発行 → 環境変数を上書きしてください
  • Stop hook は すべての Claude Code session で発火します。case にない project は *) exit 0 でサイレントに弾く設計なので、未追跡の作業内容が Recorder に紛れ込むことはありません