Skip to content

18.8 Simple Usage Monitor:Claude Code 实时用量监控工具

仓库地址: SrivathsanSivakumar/simple-usage-monitor版本: 0.1.0 许可证: MIT PyPI 包名: sumonitor

本节概览

  • 理解 Claude Code 用量监控的痛点与解决方案
  • 掌握 Simple Usage Monitor 的核心架构设计
  • 学习如何通过 pexpect 实现终端代理模式
  • 了解 Claude API 的定价机制与订阅计划限制

1. 项目背景

1.1 问题场景

使用 Claude Code 进行编程时,你是否有过这样的困扰:

  • Token 用量盲区:不知道当前会话消耗了多少 Token
  • 成本不透明:无法实时了解 API 调用的费用
  • 订阅额度焦虑:不确定距离触发限流还有多少余量
  • 频繁切换窗口:需要打开网页或其他工具查看用量

通俗比喻:这就像开车时没有油表——你知道车在跑,但不知道还能跑多远,只能等到"抛锚"(触发限流)才知道油箱空了。

1.2 Simple Usage Monitor 的解决方案

Simple Usage Monitor 提供了一个优雅的解决方案:在终端底部实时显示用量信息,让你在使用 Claude Code 的同时,随时掌握:

  • 当前会话的 Token 消耗(输入/输出/缓存)
  • 实时费用估算
  • 订阅计划的使用进度
  • 距离额度重置的剩余时间
┌─────────────────────────────────────────────────────────────────┐
│                     正常的 Claude Code 界面                      │
│                                                                 │
│  > 你的代码和对话内容...                                          │
│                                                                 │
├─────────────────────────────────────────────────────────────────┤
│ 💰 $2.34 │ ⬆️ 12.5K │ ⬇️ 8.2K │ 📊 45/250 msgs │ ⏱️ 2h 15m     │
└─────────────────────────────────────────────────────────────────┘

2. 核心特性

2.1 实时监控指标

指标说明数据来源
Token 用量输入、输出、缓存读写 Token 数~/.claude/projects/ JSONL 日志
费用估算基于模型定价的实时成本分层定价算法
消息计数当前会话的消息数量日志解析
额度进度相对于订阅计划的使用百分比计划限制配置
重置倒计时距离 5 小时窗口重置的时间时间戳计算

2.2 支持的模型与定价

python
# 定价配置($ per Million Tokens)
MODEL_PRICING = {
    "claude-opus-4-5-20251101": {
        "input": 5.00,      # 输入 Token
        "output": 25.00,    # 输出 Token
        "cache_write": 6.25,
        "cache_read": 0.50
    },
    "claude-sonnet-4-5-20251101": {
        "input": 3.00,
        "output": 15.00,
        "cache_write": 3.75,
        "cache_read": 0.30,
        "tiered": True      # 200K Token 后分级提价
    },
    "claude-haiku-4-5-20251101": {
        "input": 1.00,
        "output": 5.00,
        "cache_write": 1.25,
        "cache_read": 0.10
    }
}

2.3 订阅计划限制

根据 Claude Code 的订阅计划,每 5 小时窗口有不同的限制:

计划输出 Token 限制成本上限消息限制
Pro19,000$18.00250 条
Max588,000$35.001,000 条
Max20220,000$140.002,000 条

3. 技术架构

3.1 整体架构

┌─────────────────────────────────────────────────────────────────┐
│                        Simple Usage Monitor                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐       │
│  │   main.py    │───▶│  Terminal    │───▶│   Claude     │       │
│  │  (入口点)    │    │   Handler    │    │    Code      │       │
│  └──────────────┘    └──────────────┘    └──────────────┘       │
│         │                   │                   │                │
│         │                   │                   │                │
│         ▼                   ▼                   ▼                │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐       │
│  │  argparse    │    │   Overlay    │    │   pexpect    │       │
│  │  (命令行)    │    │   (叠加层)   │    │   (进程)     │       │
│  └──────────────┘    └──────────────┘    └──────────────┘       │
│                             │                                    │
│                             ▼                                    │
│                      ┌──────────────┐                           │
│                      │  LogReader   │                           │
│                      │  (日志解析)  │                           │
│                      └──────────────┘                           │
│                             │                                    │
│                             ▼                                    │
│                      ┌──────────────┐                           │
│                      │ ~/.claude/   │                           │
│                      │  projects/   │                           │
│                      │   *.jsonl    │                           │
│                      └──────────────┘                           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

3.2 核心模块说明

模块路径职责
main.pysrc/sumonitor/main.py程序入口,参数解析,进程编排
TerminalHandlersrc/sumonitor/terminal/终端管理,叠加层渲染,窗口适配
LogReadersrc/sumonitor/data/JSONL 日志解析,Token 统计
Pricingsrc/sumonitor/data/pricing.py模型定价,费用计算

3.3 关键设计决策

设计决策 1:pexpect 代理模式

Simple Usage Monitor 采用了"代理模式"而非"日志轮询模式":

python
# 代理模式:通过 pexpect 启动 Claude Code
spawn = pexpect.spawn(
    claude_path,
    encoding='utf-8',
    dimensions=(rows, cols)
)

为什么选择代理模式?

  • 无缝集成:用户像平常一样使用 sumonitor 命令即可
  • 实时交互:保持完整的终端交互体验
  • 窗口适配:自动处理终端大小变化(SIGWINCH)

设计决策 2:ANSI 叠加层渲染

使用 ANSI 转义序列在终端底部绘制信息:

python
def draw_overlay(self, text):
    # 保存光标位置
    sys.stdout.write('\x1b[s')
    # 移动到最后一行
    sys.stdout.write(f'\x1b[{self.rows};1H')
    # 清除当前行并写入叠加层
    sys.stdout.write('\x1b[2K' + text)
    # 恢复光标位置
    sys.stdout.write('\x1b[u')
    sys.stdout.flush()

为什么这样设计?

  • 非侵入性:不干扰正常的终端输出
  • 实时更新:每秒刷新,无闪烁
  • 光标保护:保存/恢复光标位置,用户输入不受影响

4. 代码解析

4.1 入口函数 main.py

python
def get_args_parser():
    """配置命令行参数解析器"""
    parser = argparse.ArgumentParser(
        description="Real-time token and cost monitoring for Claude Code"
    )
    parser.add_argument('--version', action='version', version='0.1.0')
    parser.add_argument('--path', help='Custom Claude Code installation path')
    parser.add_argument(
        '--plan',
        choices=['pro', 'max5', 'max20'],
        default='pro',
        help='Subscription plan for limit tracking'
    )
    return parser

def main():
    """主函数:编排整个监控流程"""
    args = get_args_parser().parse_args()

    # 1. 创建 pexpect 子进程启动 Claude Code
    spawn = pexpect.spawn(
        args.path or find_claude_binary(),
        encoding='utf-8'
    )

    # 2. 初始化日志读取器
    log_reader = LogReader()

    # 3. 初始化终端处理器(传入日志读取器和 spawn 对象)
    terminal = TerminalHandler(log_reader, spawn, args.plan)

    # 4. 设置终端尺寸
    terminal.set_dimensions(*terminal.get_terminal_size())

    # 5. 注册窗口大小变化信号处理
    signal.signal(signal.SIGWINCH, terminal.on_resize)

    # 6. 进入交互模式
    terminal.interact()

代码解读

  1. pexpect.spawn:启动 Claude Code 作为子进程,建立双向通信管道
  2. LogReader:监听 ~/.claude/projects/ 目录下的 JSONL 日志文件
  3. TerminalHandler:管理终端显示,包括叠加层渲染
  4. SIGWINCH 处理:当用户调整终端窗口大小时,自动适配

4.2 日志读取器 LogReader

python
class UsageData:
    """用量数据结构"""
    model: str
    input_tokens: int
    output_tokens: int
    cache_read_tokens: int
    cache_write_tokens: int
    cost: float
    timestamp: datetime

class LogReader:
    def __init__(self):
        self.processed_entries = set()  # 去重已处理的条目

    def get_jsonl_files(self) -> List[Path]:
        """获取当前项目相关的 JSONL 日志文件"""
        claude_dir = Path.home() / '.claude' / 'projects'
        # 返回与当前工作目录关联的日志文件
        return list(claude_dir.glob('**/conversation*.jsonl'))

    def parse_json_files(self) -> List[UsageData]:
        """解析 JSONL 文件,提取最近 120 小时的用量数据"""
        usage_list = []
        cutoff_time = datetime.now() - timedelta(hours=120)

        for file in self.get_jsonl_files():
            with open(file, 'r') as f:
                for line in f:
                    try:
                        entry = json.loads(line)
                        # 检查时间戳和去重
                        if self._should_process(entry, cutoff_time):
                            usage = self._extract_usage(entry)
                            usage_list.append(usage)
                    except json.JSONDecodeError:
                        continue

        return usage_list

    def _calculate_total_cost(self, usage: UsageData) -> float:
        """计算费用(支持分层定价)"""
        pricing = get_model_pricing(usage.model)

        # 基础费用
        cost = (
            usage.input_tokens * pricing['input'] / 1_000_000 +
            usage.output_tokens * pricing['output'] / 1_000_000
        )

        # 缓存费用
        cost += (
            usage.cache_read_tokens * pricing['cache_read'] / 1_000_000 +
            usage.cache_write_tokens * pricing['cache_write'] / 1_000_000
        )

        # 分层定价(如 Sonnet 在 200K Token 后提价)
        if pricing.get('tiered') and usage.output_tokens > 200_000:
            # 应用分层费率...
            pass

        return cost

代码解读

  • 去重机制:使用 processed_entries Set 避免重复计算同一条日志
  • 时间窗口:只处理最近 120 小时的数据(符合订阅周期逻辑)
  • 优雅降级:遇到 JSON 解析错误时跳过,不中断整体流程

4.3 终端处理器 TerminalHandler

python
class TerminalHandler:
    def __init__(self, log_reader, spawn, plan):
        self.log_reader = log_reader
        self.spawn = spawn
        self.plan = plan
        self.rows = 24
        self.cols = 80

    def get_terminal_size(self) -> Tuple[int, int]:
        """获取终端尺寸"""
        import fcntl, termios, struct
        result = fcntl.ioctl(0, termios.TIOCGWINSZ,
                            b'\x00\x00\x00\x00\x00\x00\x00\x00')
        rows, cols = struct.unpack('hh', result[:4])
        return rows, cols

    def on_resize(self, signum, frame):
        """处理终端窗口大小变化"""
        self.rows, self.cols = self.get_terminal_size()
        self.spawn.setwinsize(self.rows, self.cols)

    def get_overlay_data(self) -> str:
        """构建叠加层显示内容"""
        usage_list = self.log_reader.parse_json_files()

        # 聚合统计
        total_input = sum(u.input_tokens for u in usage_list)
        total_output = sum(u.output_tokens for u in usage_list)
        total_cost = sum(u.cost for u in usage_list)
        message_count = len(usage_list)

        # 获取计划限制
        limits = get_plan_limits(self.plan)

        # 计算重置时间
        time_to_reset = self._calculate_reset_time()

        # 格式化显示
        return (
            f"💰 ${total_cost:.2f} │ "
            f"⬆️ {total_input/1000:.1f}K │ "
            f"⬇️ {total_output/1000:.1f}K │ "
            f"📊 {message_count}/{limits['messages']} msgs │ "
            f"⏱️ {time_to_reset}"
        )

    def draw_overlay(self, text):
        """在终端底部绘制叠加层"""
        sys.stdout.write('\x1b[s')           # 保存光标
        sys.stdout.write(f'\x1b[{self.rows};1H')  # 移动到最后一行
        sys.stdout.write('\x1b[2K')          # 清除当前行
        sys.stdout.write(text)               # 写入内容
        sys.stdout.write('\x1b[u')           # 恢复光标
        sys.stdout.flush()

代码解读

  • TIOCGWINSZ:通过 ioctl 系统调用获取终端的实际尺寸
  • ANSI 转义序列\x1b[s(保存光标)、\x1b[u(恢复光标)、\x1b[2K(清行)
  • 非阻塞更新:叠加层在独立线程中每秒刷新,不影响主交互

5. 安装与使用

5.1 安装方法

方法 1:通过 PyPI 安装(推荐)

bash
pip install sumonitor

方法 2:从源码安装

bash
git clone https://github.com/SrivathsanSivakumar/simple-usage-monitor.git
cd simple-usage-monitor
pip install .

方法 3:使用 pipx(隔离环境)

bash
pipx install sumonitor

5.2 使用示例

基础用法

bash
# 启动监控(使用默认 Pro 计划)
sumonitor

指定订阅计划

bash
# Max5 订阅用户
sumonitor --plan max5

# Max20 订阅用户
sumonitor --plan max20

自定义 Claude Code 路径

bash
sumonitor --path /custom/path/to/claude

5.3 显示效果

启动后,你会看到正常的 Claude Code 界面,但在终端底部多了一行监控信息:

┌─────────────────────────────────────────────────────────────────┐
│ claude-code-os-x> 帮我写一个快速排序算法                          │
│                                                                 │
│ 我来为你实现一个快速排序算法...                                   │
│                                                                 │
│ ```python                                                       │
│ def quicksort(arr):                                             │
│     if len(arr) <= 1:                                           │
│         return arr                                              │
│     ...                                                         │
├─────────────────────────────────────────────────────────────────┤
│ 💰 $0.42 │ ⬆️ 2.1K │ ⬇️ 1.8K │ 📊 12/250 msgs │ ⏱️ 4h 32m      │
└─────────────────────────────────────────────────────────────────┘

6. 技术亮点与局限性

6.1 技术亮点

特性说明
零配置自动检测 Claude Code 安装路径和项目目录
本地处理所有数据在本地处理,保护隐私
实时更新每秒刷新,无需手动触发
窗口自适应自动响应终端大小变化
轻量依赖仅依赖 pexpect(Python 标准库之外)

6.2 局限性

局限原因
仅支持 Claude Code依赖特定的日志格式和路径
定价可能过时硬编码的定价需要手动更新
仅限终端不支持 IDE 集成环境
Unix-like 系统pexpect 在 Windows 上支持有限

6.3 与类似工具对比

工具Simple Usage MonitorClaude Code Usage Monitor
安装方式pip/PyPI源码
显示位置终端内嵌独立窗口
更新频率实时(1s)定时刷新
订阅计划支持✅ Pro/Max5/Max20部分

7. 延伸学习

7.1 相关技术点

  • pexpect 库:Python 的进程交互库,用于自动化和测试
  • ANSI 转义序列:终端控制的标准协议,了解更多可查阅 ANSI escape code
  • JSONL 格式:每行一个 JSON 对象,适合日志和流式处理
  • Claude API 定价官方定价页面

7.2 可以探索的改进方向

  1. 跨平台支持:使用 winptyConPTY 支持 Windows
  2. 历史趋势图表:集成 richtextual 显示用量趋势
  3. 配置文件支持:允许用户自定义显示格式和阈值告警
  4. IDE 插件:开发 VS Code 扩展版本

8. 小结

Simple Usage Monitor 是一个精巧的 CLI 工具,通过 pexpect 代理模式和 ANSI 叠加层技术,实现了在不干扰正常工作流的前提下,为 Claude Code 用户提供实时的用量监控能力。

核心收获

  1. 代理模式设计:通过 pexpect 启动子进程,实现透明代理
  2. ANSI 终端控制:使用转义序列实现非侵入式的 UI 叠加
  3. 日志解析架构:JSONL 格式的流式处理与去重机制
  4. 订阅感知定价:支持多种订阅计划的差异化限制

这个项目展示了如何用约 500 行 Python 代码解决一个实际的开发者痛点,是学习终端工具开发和 Claude API 集成的优秀案例。


下一节:18.9 Claude Code 进阶技巧

基于 MIT 许可证发布。内容版权归作者所有。