Tokmon -- 监控 Claude Code 自己的 Token 消耗

我用 Claude Code 写了一个 macOS 原生 App,来监控 Claude Code 自己的 Token 消耗

用 SwiftUI + Swift Charts 打造了一个本地 Token 用量监控工具 Tokmon,零依赖、纯离线、开箱即用。

起因

最近重度使用 Claude Code 做开发,每天的 token 消耗量非常大。虽然有 npx ccusage@latest 这样的命令行工具可以查看用量,但每次都要跑一遍命令,而且输出是纯文本表格,不够直观。

我想要的是:

  • 一眼看到每天花了多少钱
  • 知道哪个项目最烧钱
  • 了解缓存命中率(这直接影响费用)
  • 不同模型的用量分布

于是决定用 SwiftUI 写一个 macOS 原生 App。整个过程从设计到开发到上架 GitHub,全程用 Claude Code 完成。

先看效果

Dashboard --- 一眼掌握全局

顶部四个指标卡片:总费用、总 Token、缓存命中率、会话数。中间是每日用量柱状图(鼠标悬停有 tooltip),右侧是模型分布饼图。

Projects --- 哪个项目最烧钱

堆叠面积图展示各项目的 Token 消耗趋势,下方表格可以展开查看每个会话的详情。

Models --- 模型用量对比

按模型维度统计费用和 Token 分布。Opus 果然是大头。

Sessions --- 会话级明细

所有会话按时间排序,支持搜索和排序,点进去可以看到 Token 时间线、模型切换、工具调用日志。

技术方案

数据从哪来?

Claude Code 会在本地 ~/.claude/projects/ 目录下,为每个项目、每个会话生成 JSONL 文件。每一行是一个 JSON 对象,记录了:

json 复制代码
{
  "type": "assistant",
  "message": {
    "model": "claude-opus-4-6",
    "usage": {
      "input_tokens": 3,
      "output_tokens": 9,
      "cache_creation_input_tokens": 44874,
      "cache_read_input_tokens": 0
    }
  },
  "timestamp": "2026-03-19T12:14:17.775Z",
  "sessionId": "7e175846-a0c7-4fcb-9f3f-5aaed25b5f4b",
  "cwd": "/xxx",
  "gitBranch": "xxx/xxx"
}

关键字段:

  • usage --- 四种 token 计数:input、output、cache_creation、cache_read
  • model --- 使用的模型(opus/sonnet/haiku 等)
  • sessionId + timestamp --- 会话关联和时间线
  • cwd + gitBranch --- 项目和分支信息

技术栈

组件 选择 理由
UI 框架 SwiftUI macOS 原生,声明式 UI
图表 Swift Charts Apple 官方图表库,和 SwiftUI 无缝集成
数据层 纯内存模型 数据量不大,不需要数据库
项目管理 XcodeGen YAML 配置生成 xcodeproj,方便版本控制
最低版本 macOS 14 Swift Charts 的 SectorMark 需要
第三方依赖 零依赖,纯 Apple 框架

核心架构

bash 复制代码
App
├── AppState (ObservableObject)     # 全局状态 + 时间范围过滤
├── JSONLParser (Actor)             # 异步解析所有 JSONL 文件
├── StatsEngine                     # 按日期/项目/模型聚合统计
├── ModelPricing                    # 可自定义的模型定价
└── L10n                            # 中英文国际化

数据流 :App 启动 → JSONLParser 扫描 ~/.claude/projects/ → 解析所有 .jsonl 文件(包括子代理) → 构建 Project/Session 模型 → AppState 持有数据 → 各 View 通过 @EnvironmentObject 消费。

费用计算

费用计算基于 Anthropic 官方定价,四种 token 类型分别计价:

swift 复制代码
static func cost(for model: String, usage: TokenUsage) -> Double {
    let price = priceFor(model)
    let input = Double(usage.inputTokens) / 1_000_000 * price.inputPerMillion
    let output = Double(usage.outputTokens) / 1_000_000 * price.outputPerMillion
    let cacheWrite = Double(usage.cacheCreationTokens) / 1_000_000 * price.cacheWritePerMillion
    let cacheRead = Double(usage.cacheReadTokens) / 1_000_000 * price.cacheReadPerMillion
    return input + output + cacheWrite + cacheRead
}

当前定价(2026年3月):

模型 Input Output Cache Write Cache Read
Opus 4.6 $5/MTok $25/MTok $6.25/MTok $0.50/MTok
Sonnet 4.6 $3/MTok $15/MTok $3.75/MTok $0.30/MTok
Haiku 4.5 $1/MTok $5/MTok $1.25/MTok $0.10/MTok

用户可以在 Settings 里自定义每个模型的单价。

图表交互:鼠标跟随 Tooltip

Swift Charts 本身不提供 hover tooltip,需要用 chartOverlay + onContinuousHover 手动实现:

swift 复制代码
.chartOverlay { proxy in
    GeometryReader { geo in
        Rectangle().fill(.clear).contentShape(Rectangle())
            .onContinuousHover { phase in
                switch phase {
                case .active(let loc):
                    hoverLocation = loc
                    chartWidth = geo.size.width
                    if let date: Date = proxy.value(atX: loc.x) {
                        selectedDate = Calendar.current.startOfDay(for: date)
                    }
                case .ended:
                    selectedDate = nil
                }
            }
    }
}

Tooltip 位置需要处理边界情况 --- 鼠标靠右时 tooltip 要翻转到左侧:

swift 复制代码
let tooltipW: CGFloat = 170
let xOff: CGFloat = hoverLocation.x + tooltipW + 20 > chartWidth
    ? hoverLocation.x - tooltipW - 12  // 翻转到左侧
    : hoverLocation.x + 16             // 正常在右侧

项目名解析

Claude Code 的项目目录名是把文件路径中的 / 替换成 -,比如:

css 复制代码
-Users-devsh-Documents-cnb-code-app-backend

需要解析回可读的项目名。策略是跳过常见路径前缀(Users、Documents 等),取最后有意义的部分:

swift 复制代码
static func decodeProjectName(_ dirName: String) -> String {
    let components = dirName.components(separatedBy: "-").filter { !$0.isEmpty }
    let skipPrefixes = ["Users", "Documents", "var", "folders", "private", "tmp", "T"]
    // ... 跳过前缀,返回最后 1-2 个有意义的段
}

CI/CD:自动构建 DMG

用 GitHub Actions 实现打 tag 自动构建 DMG 并发布到 Releases:

yaml 复制代码
on:
  push:
    tags: ['v*']

permissions:
  contents: write

jobs:
  build:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
      - run: brew install xcodegen
      - run: xcodegen generate
      - run: xcodebuild -project Tokmon.xcodeproj -scheme Tokmon -configuration Release -derivedDataPath build
      - run: |
          mkdir dmg_contents
          cp -R build/Build/Products/Release/Tokmon.app dmg_contents/
          ln -s /Applications dmg_contents/Applications
          hdiutil create -volname "Tokmon" -srcfolder dmg_contents -ov -format UDZO "Tokmon-${GITHUB_REF_NAME}.dmg"
      - uses: softprops/action-gh-release@v2
        with:
          files: Tokmon-*.dmg

发版只需要:

bash 复制代码
git tag -a v1.0.0 -m "v1.0.0" && git push origin v1.0.0

一些发现

通过 Tokmon 分析自己的使用数据,发现了几个有意思的点:

  1. 缓存命中率约 46% --- 说明将近一半的 input token 是从缓存读取的,省了不少钱
  2. Opus 占了 83% 的费用 --- 虽然 Haiku 的会话数更多,但 Opus 单价高太多
  3. 日均消耗 $250+ --- 重度使用 Claude Code 的成本确实不低
  4. 不同项目差异很大 --- web-management 项目消耗最多,因为代码量大、上下文长

使用方式

直接下载

GitHub Releases 下载 DMG,拖到 Applications 即可。

从源码构建

bash 复制代码
brew install xcodegen
git clone https://github.com/learningpro/Tokmon.git
cd Tokmon
xcodegen generate
open Tokmon.xcodeproj  # Cmd+R 运行

开源

项目完全开源,MIT 协议:github.com/learningpro...

欢迎 Star、Issue、PR。如果你也在重度使用 Claude Code,希望这个工具能帮你更好地了解自己的用量和费用。


整个项目从设计到开发到发布,全程使用 Claude Code 完成。包括 UI 设计稿(用 Gemini 生成)、SwiftUI 代码、JSONL 解析、图表交互、国际化、CI/CD 配置、App 图标、README,甚至这篇文章。

相关推荐
小码哥_常2 小时前
Spring项目新姿势:Lambda封装Service调用,告别繁琐注入!
后端
不能放弃治疗3 小时前
详解大模型对话 API,messages 角色 system 、user、assistant、tool
后端
hutengyi4 小时前
go测试问题记录
开发语言·后端·golang
青槿吖4 小时前
第二篇:Spring Boot进阶:整合异常处理、测试、多环境与日志,开发稳得一批!
java·spring boot·后端·spring·面试·sqlserver·状态模式
武子康4 小时前
大数据-254 离线数仓 - Airflow 任务调度与工作流管理实战
大数据·后端·apache hive
pip install USART4 小时前
容器化场景常用kubectl命令
后端·容器·kubernetes
华科易迅4 小时前
Spring装配对象方法-构造方法
java·后端·spring
紫丁香5 小时前
高并发面试4
后端·面试·高并发
精神小伙就是猛5 小时前
使用go-zero快速搭建一个微服务(一)
开发语言·后端·微服务·golang