這個章節要説明關於 Claude Code Analytics 的部分内容

計算 token 費用、分析工具使用模式、建立 Web Dashboard

首先加入 Token 計算器

這邊要查詢所有 session,計算每個 session 的 token 用量與美金費用

新增費用計算模組

新增 lib/costs.mjs

const PRICING = {
  'claude-opus-4':   { in: 15,   out: 75,   cw: 18.75, cr: 1.50 },
  'claude-sonnet-4': { in: 3,    out: 15,   cw: 3.75,  cr: 0.30 },
  'claude-haiku-4':  { in: 0.80, out: 4,    cw: 1.00,  cr: 0.08 },
};

/**
 * 從 model ID 推斷定價方案
 */
function getPricing(modelId) {
  const id = modelId?.toLowerCase() ?? '';
  if (id.includes('opus'))  return PRICING['claude-opus-4'];
  if (id.includes('haiku')) return PRICING['claude-haiku-4'];
  return PRICING['claude-sonnet-4'];
}

/**
 * 計算單條 assistant 訊息的費用
 */
export function calcMessageCost(msg) {
  if (msg.type !== 'assistant') return 0;
  const usage = msg.message?.usage;
  if (!usage) return 0;

  const p = getPricing(msg.message?.model);
  const M = 1_000_000;
  return (
    (usage.input_tokens ?? 0) / M * p.in +
    (usage.output_tokens ?? 0) / M * p.out +
    (usage.cache_creation_input_tokens ?? 0) / M * p.cw +
    (usage.cache_read_input_tokens ?? 0) / M * p.cr
  );
}

/**
 * 計算整個 session 的 token 統計與費用
 */
export function calcSessionStats(messages) {
  let input = 0, output = 0, cacheWrite = 0, cacheRead = 0, costUSD = 0;
  let models = new Set();
  let toolCalls = 0, toolErrors = 0;

  for (const msg of messages) {
    if (msg.type !== 'assistant') continue;

    const usage = msg.message?.usage;
    if (usage) {
      input     += usage.input_tokens ?? 0;
      output    += usage.output_tokens ?? 0;
      cacheWrite += usage.cache_creation_input_tokens ?? 0;
      cacheRead  += usage.cache_read_input_tokens ?? 0;
      costUSD   += calcMessageCost(msg);
    }

    if (msg.message?.model) models.add(msg.message.model);

    for (const c of msg.message?.content ?? []) {
      if (c.type === 'tool_use') toolCalls++;
    }
  }

  // 計算 tool 錯誤數(在 user 的 tool_result 中)
  for (const msg of messages) {
    if (msg.type !== 'user') continue;
    for (const c of msg.message?.content ?? []) {
      if (c.type === 'tool_result' && c.is_error) toolErrors++;
    }
  }

  return {
    input_tokens: input,
    output_tokens: output,
    cache_write_tokens: cacheWrite,
    cache_read_tokens: cacheRead,
    total_tokens: input + output + cacheWrite + cacheRead,
    cost_usd: costUSD,
    models: [...models],
    tool_calls: toolCalls,
    tool_errors: toolErrors,
  };
}

新增 Token 報告腳本

新增 token-report.mjs

import { scanProjects, scanProjectSessions } from './lib/scanner.mjs';
import { parseJsonl } from './lib/parser.mjs';
import { calcSessionStats } from './lib/costs.mjs';

const DAYS = parseInt(process.argv[2] ?? '30', 10);
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - DAYS);

console.log(`\n Claude Code Token 報告(過去 ${DAYS} 天)\n`);

let totalCost = 0;
let totalTokens = 0;
let totalSessions = 0;
const projectStats = [];

for (const proj of scanProjects()) {
  let projCost = 0;
  let projTokens = 0;
  let projSessions = 0;

  for (const session of scanProjectSessions(proj)) {
    const sessionStart = new Date(session.first_message_time);
    if (sessionStart < cutoff) continue;

    const messages = parseJsonl(session.file_path);
    const stats = calcSessionStats(messages);

    projCost    += stats.cost_usd;
    projTokens  += stats.total_tokens;
    projSessions++;
  }

  if (projSessions === 0) continue;

  projectStats.push({
    name: proj.name.slice(0, 35),
    sessions: projSessions,
    tokens: projTokens,
    cost: projCost,
  });

  totalCost    += projCost;
  totalTokens  += projTokens;
  totalSessions += projSessions;
}

// 排序並輸出
projectStats.sort((a, b) => b.cost - a.cost);

console.log('專案費用排行:\n');
console.log('  ' + '專案名稱'.padEnd(36) + 'Sessions'.padStart(9) + '  Tokens'.padStart(12) + '  費用 (USD)'.padStart(12));
console.log('  ' + '─'.repeat(72));

for (const p of projectStats) {
  const name = p.name.padEnd(36);
  const sess = String(p.sessions).padStart(9);
  const tok  = p.tokens.toLocaleString().padStart(12);
  const cost = `$${p.cost.toFixed(4)}`.padStart(12);
  console.log(`  ${name}${sess}  ${tok}  ${cost}`);
}

console.log('\n' + '  ' + '─'.repeat(72));
console.log(`  ${'合計'.padEnd(36)}${String(totalSessions).padStart(9)}  ${totalTokens.toLocaleString().padStart(12)}  ${'$'+totalCost.toFixed(4)}`);

// 平均費用
if (totalSessions > 0) {
  console.log(`\n平均每 session 費用:$${(totalCost / totalSessions).toFixed(4)}`);
  console.log(`平均每天費用:       $${(totalCost / DAYS).toFixed(4)}`);
  console.log(`估算月費用:         $${(totalCost / DAYS * 30).toFixed(2)}`);
}

過去 7 天

node token-report.mjs 7

專案費用排行:

  專案名稱                                 Sessions      Tokens    費用 (USD)
  ────────────────────────────────────────────────────────────────────────
  D--OlgCase-R****R-3                        7   184,270,495     $498.5703
  D--Git-Cla*-***deEg                         2   102,513,672     $252.1710
  d--Git-obsi-w****e-brain                 3    89,158,775     $248.8697
...
  ────────────────────────────────────────────────────────────────────────
  合計                                         28   463,693,230  $1297.4101

平均每 session 費用:$46.3361
平均每天費用:       $185.3443
估算月費用:         $5560.33

這章節新增 Session Analytics

分析工具使用頻率統計,加上用活躍時段直方圖來表現

新增 Analytics 模組

新增 lib/analytics.mjs

import { parseJsonl } from './parser.mjs';

/**
 * 提取所有工具呼叫
 */
export function extractToolCalls(messages) {
  const calls = [];
  for (const msg of messages) {
    if (msg.type !== 'assistant') continue;
    for (const c of msg.message?.content ?? []) {
      if (c.type === 'tool_use') {
        calls.push({ name: c.name, timestamp: msg.timestamp });
      }
    }
  }
  return calls;
}

/**
 * 計算工具使用頻率
 */
export function toolFrequency(sessions) {
  const freq = {};
  for (const { messages } of sessions) {
    for (const call of extractToolCalls(messages)) {
      freq[call.name] = (freq[call.name] ?? 0) + 1;
    }
  }
  return Object.entries(freq)
    .sort(([, a], [, b]) => b - a)
    .map(([name, count]) => ({ name, count }));
}

/**
 * 計算活躍時段(0-23 時)
 */
export function activeHours(sessions) {
  const hours = new Array(24).fill(0);
  for (const { first_message_time } of sessions) {
    const h = new Date(first_message_time).getHours();
    hours[h]++;
  }
  return hours;
}

/**
 * 計算活躍星期(0=週日...6=週六)
 */
export function activeDays(sessions) {
  const days = new Array(7).fill(0);
  const dayNames = ['週日','週一','週二','週三','週四','週五','週六'];
  for (const { first_message_time } of sessions) {
    const d = new Date(first_message_time).getDay();
    days[d]++;
  }
  return days.map((count, i) => ({ day: dayNames[i], count }));
}

/**
 * 命令列直方圖(最大寬度 30 格)
 */
export function cliHistogram(data, labelKey, valueKey, title) {
  const max = Math.max(...data.map(d => d[valueKey]), 1);
  console.log(`\n${title}\n`);
  for (const item of data) {
    const bar = '█'.repeat(Math.round(item[valueKey] / max * 30));
    const label = String(item[labelKey]).padStart(6);
    console.log(`  ${label} ${bar.padEnd(30)} ${item[valueKey]}`);
  }
}

新增 Analytics 報告腳本

新增 analytics-report.mjs

import { scanProjects, scanProjectSessions } from './lib/scanner.mjs';
import { parseJsonl } from './lib/parser.mjs';
import { toolFrequency, activeHours, activeDays, cliHistogram } from './lib/analytics.mjs';

// 載入所有近 30 天的 sessions
const DAYS = 30;
const cutoff = new Date();
cutoff.setDate(cutoff.getDate() - DAYS);

const allSessions = [];

for (const proj of scanProjects()) {
  for (const session of scanProjectSessions(proj)) {
    if (new Date(session.first_message_time) < cutoff) continue;
    const messages = parseJsonl(session.file_path);
    allSessions.push({ ...session, messages });
  }
}

console.log(`\n Claude Code 使用分析(過去 ${DAYS} 天,${allSessions.length} 個 sessions)\n`);

// 1. 工具頻率
const freqData = toolFrequency(allSessions).slice(0, 12);
cliHistogram(freqData, 'name', 'count', ' 工具使用頻率 TOP 12');

// 2. 活躍時段
const hours = activeHours(allSessions);
const hourData = hours.map((count, h) => ({ label: `${String(h).padStart(2,'0')}:00`, count }));
cliHistogram(hourData, 'label', 'count', ' 活躍時段分析(每小時 session 數)');

// 3. 活躍星期
const dayData = activeDays(allSessions);
cliHistogram(dayData, 'day', 'count', ' 活躍星期分析');

// 4. 最高效 session(工具數 / 來回輪數 比例)
const ranked = allSessions
  .map(s => {
    const turns = s.messages.filter(m => m.type === 'assistant').length;
    const tools = s.messages.flatMap(m =>
      m.type === 'assistant' ? (m.message?.content ?? []).filter(c => c.type === 'tool_use') : []
    ).length;
    return { ...s, turns, tools, efficiency: turns > 0 ? tools / turns : 0 };
  })
  .sort((a, b) => b.efficiency - a.efficiency)
  .slice(0, 5);

console.log('\n 工具密度最高的 Sessions(每輪工具數)\n');
for (const s of ranked) {
  const date = new Date(s.first_message_time).toLocaleDateString();
  const summary = (s.summary ?? '(無摘要)').slice(0, 50);
  console.log(`  [${date}] ${s.turns}輪  ${s.tools}個工具  效率:${s.efficiency.toFixed(1)}`);
  console.log(`           ${summary}`);
}

結果如下:


 Claude Code 使用分析(過去 30 天,129 個 sessions)


 工具使用頻率 TOP 12

    Bash ██████████████████████████████ 1730
    Read █████████████████████████████  1699
    Edit █████████████████████████      1463
   Write ███████████████                838
    Grep ██████████████                 829
  TaskUpdate ███████                        417
    Glob ████                           244
  TaskCreate ████                           224
  ToolSearch ███                            159
  AskUserQuestion ███                            148
  mcp__UnityMCP__read_console ██                             142
   Agent ██                             126

 活躍時段分析(每小時 session 數)

   00:00                                0
   01:00                                0
   02:00                                0
   03:00                                0
   04:00                                0
   05:00                                0
   06:00                                0
   07:00                                0
   08:00                                0
   09:00 ███████                        7
   10:00 ██████████████████████████████ 30
   11:00 █████                          5
   12:00 ██████                         6
   13:00 ████████                       8
   14:00 ██████████                     10
   15:00 █████████                      9
   16:00 ████████████                   12
   17:00 █████████████████              17
   18:00 █████████                      9
   19:00 ██████████████                 14
   20:00 █                              1
   21:00 █                              1
   22:00                                0
   23:00                                0

 活躍星期分析

      週日                                0
      週一 ██████████████████████████████ 39
      週二 ███████████████                19
      週三 ███████████                    14
      週四 █████████████████              22
      週五 ██████████████████             24
      週六 ████████                       11

⚡ 工具密度最高的 Sessions(每輪工具數)

  [2026/5/11] 30輪  24個工具  效率:0.8
           (無摘要)
  [2026/5/13] 321輪  236個工具  效率:0.7
           (無摘要)
  [2026/5/14] 137輪  100個工具  效率:0.7
           (無摘要)
  [2026/5/21] 22輪  16個工具  效率:0.7
           (無摘要)
  [2026/5/28] 51輪  37個工具  效率:0.7
           (無摘要)

最後面建立 Dashboard HTML

這邊就不多説了直接展示效果

node server.mjs
# 開啟瀏覽器:http://localhost:3000/dashboard.html

71925-w02m99hrvq.png

無標籤

關注作者:

新增評論