我用 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_readmodel--- 使用的模型(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 分析自己的使用数据,发现了几个有意思的点:
- 缓存命中率约 46% --- 说明将近一半的 input token 是从缓存读取的,省了不少钱
- Opus 占了 83% 的费用 --- 虽然 Haiku 的会话数更多,但 Opus 单价高太多
- 日均消耗 $250+ --- 重度使用 Claude Code 的成本确实不低
- 不同项目差异很大 --- 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,甚至这篇文章。