☰
深入 tokentab 日志解析:Claude Code、Codex、Gemini 三种会话格式各踩什么坑
2026/10/13 22:58:26 网站建设 项目流程

【免费下载链接】tokentab

A CLI that reads Claude Code, Codex, and Gemini CLI session logs and works out how much they cost, by model, project, and day.

项目地址:https://gitcode.com/gh_mirrors/to/tokentab
点击查看免费下载

tokentab是一个命令行工具,读取 Claude Code、Codex、Gemini CLI 自动写在本地磁盘上的会话日志,统计 token 用量和花费,并按模型、项目、日期、活动类型拆分——全程本地运行,不需要 API key,数据不出机器。这个项目最有意思的部分就是日志解析:三家工具的日志格式、token 计数口径各不相同,各有各的坑。这篇文章带你走进 tokentab/providers/ 目录,看看解析器分别为每种格式踩了什么坑、又是怎么绕过去的。

一、先看日志在哪:三家工具的落盘习惯

tokentab 不依赖任何网络接口,全部数据来自这些工具自己留下的日志文件:

工具日志位置文件格式
Claude Code~/.claude/projects/**/*.jsonl每行一个 JSON 对象(JSONL)
Codex~/.codex/sessions/**/rollout-*.jsonl逐事件 JSONL,token 为累计值
Gemini CLI~/.gemini/tmp/**/session-*.json整个会话一个 JSON 文档

每个工具对应tokentab/providers/下一个独立模块(claude.py、codex.py、gemini.py),只需暴露一个collect()函数。工具没装就返回空列表,自动跳过——你只会看到自己实际用过的工具。

二、Claude Code:最友好的格式,坑在缓存字段

Claude Code 是三个格式里最规整的,源码注释直接称它为 "friendliest format":一次会话一个.jsonl文件,每行一个 JSON 对象,assistant 回合自带模型名和 usage 块。

它的主要坑在于token 缓存被拆成独立字段。Claude 把缓存读取和缓存写入分开记录,如果不拆开来读,就会把"便宜的缓存命中"和"较贵的缓存写入"混成一笔糊涂账:

tokens=TokenCounts( input=usage.get("input_tokens", 0), output=usage.get("output_tokens", 0), cache_read=usage.get("cache_read_input_tokens", 0), cache_write=usage.get("cache_creation_input_tokens", 0), )

对应源码:claude.py。

另一个细节是两遍扫描 + 去重(claude.py):

  • 第一遍收集第一条用户消息和tool_use工具名,用于活动分类(见 classify.py);
  • 第二遍才提取 usage 块,并用消息id去重,防止同一次调用被计入两遍。

项目名也不是白拿的:会话文件路径里的项目目录名是被工具"把斜杠换成连字符"编码过的字符串,pretty_project()无法无损还原,只能取最后一段有意义的片段当标签(比如显示my-app而不是一长串编码)。

三、Codex:累计 token 必须做差,事件结构三层嵌套

Codex 的日志是事件流,最大的坑在计数口径:token_count事件里存的是整场会话的累计值。如果直接把每条事件加起来,token 会被重复计算 N 次。解析器的做法是对相邻事件做差,得到每次调用的增量:

d_in = max(0, tot_in - prev_in) d_out = max(0, tot_out - prev_out) d_cache = max(0, tot_cache - prev_cache)

max(0, …)保证计数器被重置(比如新开会话)时不会出现负数。对应源码:codex.py。

第二个坑是事件结构不一致:token_count可能直接出现在顶层,也可能包在payload或info里。_find_token_count()一次兼容了三种可能的位置(codex.py),否则换个版本的 Codex 就会读不到数据。

还有两个"顺手"的坑:

  • 工作目录来自文件开头附近的 session-meta 事件,得先做一遍"上下文扫描"才能知道这条记录属于哪个项目;
  • 老会话存放在archived_sessions/目录,和sessions/都要扫描,不然历史记录会凭空消失。

四、Gemini CLI:格式最"文明",坑在输入包含缓存

Gemini CLI 的格式最规整:一个会话一个 JSON 文件,messages数组里每条消息自带tokens对象。解析起来最省心,但它埋了一个静默的坑:input字段的口径是"包含缓存"的。

如果不做处理,缓存部分会在input和cached里各被计费一次——同样的 token 付两份钱。解析器在计费前先扣回去:

fresh_input = max(0, raw_input - cached) # input is inclusive of cache

对应源码:gemini.py。这正是 README 里提到的 "pulls the cached tokens back out before pricing"。

另外两个小坑:

  • 消息缺model字段时兜底为gemini-2.5-pro(gemini.py);
  • 项目名取自tmp/后的目录层级(gemini.py)。

五、三家共通的"防御式"解析技巧 🧩

格式各不相同,但防御烂数据的思路高度一致,公共工具都放在 shared.py:

工具函数解决的问题
try_json()单行 JSON 解析失败时返回None而不是抛异常,一行坏数据不会搞挂整场统计
walk_files()遍历日志目录,跳过任何读不了的文件
to_date()同时兼容秒/毫秒时间戳(数值大于1e12视为毫秒)和带Z后缀的 ISO 字符串,失败时回退到纪元时间
pretty_project()把编码过的项目目录名还原成可读标签

对应源码:shared.py。

更外一层,providers/init.py 给每个工具的collect()都包了try/except:某个工具的日志坏了,只打印一条 warning,整份报告照常出——"一个坏掉的工具不该拖垮整个报表"。

六、解析之后:一切归一成 UsageRecord 📊

三家解析器输出的是同一个数据形状UsageRecord(定义在 types.py):provider、model、project、timestamp、activity、tokens、id。下游的计价、分组、Web 仪表盘只接触这一个结构,这也是"加一个新工具只需写一个解析模块"能成立的原因。

两个值得了解的下游设计:

  • 活动分类是纯规则(classify.py):根据用过的工具名(编辑类/搜索类)加首条消息里的关键词(fix、refactor、test…)猜出 debugging / refactor / testing / chat 等标签,没有模型调用,快且可人工核对;
  • 价格表是手工维护(prices.py):按"美元 / 百万 token"查表,模型名做模糊匹配(claude-opus-4-6-20260514也能匹配到claude-opus-4-6)。匹配不上的模型会显示$0.00并明确提示,而不是静默按免费算。

七、小结:踩坑清单与扩展方向

格式核心坑解法
Claude Code缓存读/写分字段;消息可能重复分字段读取 + 按消息 id 去重
Codextoken 是累计值;事件嵌套位置不固定相邻事件做差 + 多位置兼容查找
Gemini CLIinput含缓存 token计费前扣掉cached
三家共通坏行、坏时间戳、编码项目名防御式解析,失败降级不崩溃

想动手扩展的话,仓库留了一个现成的练习位:cursor.py 是未实现的 stub——Cursor 的数据存在 SQLite 数据库state.vscdb里而非文本日志,按文件里的 TODO 补上解析逻辑、注册进 ALL_PROVIDERS,计价和报告就自动生效了。

安装也只需三步,装完得到一条tokentab命令,直接跑即可看到最近 7 天的账单:

git clone https://gitcode.com/gh_mirrors/to/tokentab cd tokentab pip install .

整个项目的第三方依赖只有一个rich(终端表格),其余解析器、JSON 处理、Web 服务器全部是 Python 标准库——这也意味着读它的全部源码,不会遇到任何框架黑盒。

【免费下载链接】tokentab

A CLI that reads Claude Code, Codex, and Gemini CLI session logs and works out how much they cost, by model, project, and day.

项目地址:https://gitcode.com/gh_mirrors/to/tokentab
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询