Skip to content
源码分析手册

调试子系统 (Debug Subsystem)

在 Claude Code 这样复杂的 CLI 工具中,当模型“发疯”或者工具调用失败时,仅仅看终端输出是不够的。Claude Code 内置了一套极其详尽的调试子系统,它记录了模型的所有 API 往返,同时涵盖启动性能分析和针对开发者的分类过滤功能。

先搞清楚这是什么

它是一个分层日志与遥测系统。它将运行时状态分为不同级别(verbose 到 error),并提供了多种非侵入式的观测手段。对于 Anthropic 内部员工(Ants),它是一个自动开启的“飞行记录仪”;对于外部用户,它是一个可随时唤醒的故障诊断助手。

代码里的真实逻辑

  1. 五级日志体系: 在 src/utils/debug.ts 中定义了 verbose, debug, info, warn, error 五种级别。默认最小级别是 debug,只有设置 CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose 才会记录高频的底层信息(如 shell 命令的完整 stdout/stderr)。

  2. 激活模式与动态开关: 你可以通过 --debug 标志或 DEBUG=1 环境变量在启动时开启。最精妙的是 /debug 斜杠命令(由 src/skills/bundled/debug.ts 实现),它能在会话中途动态开启调试模式。即使你启动时没带参数,发现不对劲时输入 /debug,它会立即激活 BufferedWriter 开始记录后续操作。

  3. 模式过滤 (Pattern Filtering)src/utils/debugFilter.ts 提供了一种类似 DEBUG 环境变量但更强大的语法:

    • --debug=api,hooks:只看 API 调用和生命周期钩子。
    • --debug=!mcp,!file:排除 MCP 和文件操作相关的噪音。 系统会自动从日志消息前缀(如 [API]MCP server "name":)中提取类别进行匹配。
  4. 智能写入策略

    • Ants 模式:内部员工默认开启日志,使用 BufferedWriter 以 1 秒为间隔异步刷新到磁盘,确保不影响交互性能。
    • Debug 模式:通过参数开启时,切换为同步写入(appendFileSync),防止程序异常退出时丢失关键证据。
    • 软链接:每次会话都会在 ~/.claude/debug/ 下生成唯一 ID 的日志文件,并自动更新一个 latest 软链接,方便开发者直接 tail -f ~/.claude/debug/latest
  5. 启动性能分析 (Startup Profiler)src/utils/startupProfiler.ts 会测量 import_time(代码加载)、init_time(系统初始化)和 settings_time(配置读取)。Ants 会 100% 采集并上报,普通用户则只有 0.5% 的采样率用于改进产品。

边界条件

  • 隐私保护:外部用户除非显式开启调试,否则不会产生本地调试日志。这保证了敏感代码不会在用户不知情的情况下被记录。
  • API 请求缓存src/utils/log.ts 中的 captureAPIRequest 在 Ants 模式下会完整保留 API 的 messages 数组(包括上下文和 CLAUDE.md 注入的内容),而外部用户仅保留请求参数(不含消息正文),以平衡调试需求与内存消耗。
  • 日志自动清理:虽然系统提供了软链接,但日志文件本身是随会话生成的。长期使用 Claude Code 可能会在 ~/.claude/debug 下积累大量几百 KB 到几 MB 的文本文件。

接下来看什么

  • 如果你想看 API 是如何被封装并发送的,请阅读 src/utils/api.ts
  • 如果你想看报错是如何被分类处理的,请阅读 src/utils/errors.ts

源码锚点

  • src/utils/debug.ts: 核心调试逻辑、日志路径管理、写入器实现。
📄 src/utils/debug.ts — 核心调试逻辑、日志路径管理、写入器实现。L18-26 of 269
typescript
export type DebugLogLevel = 'verbose' | 'debug' | 'info' | 'warn' | 'error'

const LEVEL_ORDER: Record<DebugLogLevel, number> = {
  verbose: 0,
  debug: 1,
  info: 2,
  warn: 3,
  error: 4,
}
  • src/utils/debugFilter.ts: 日志类别提取与 inclusive/exclusive 过滤算法。
📄 src/utils/debugFilter.ts — 日志类别提取与 inclusive/exclusive 过滤算法。L3-7 of 158
typescript
export type DebugFilter = {
  include: string[]
  exclude: string[]
  isExclusive: boolean
}
  • src/utils/startupProfiler.ts: 基于 perf_hooks 的性能埋点实现。
📄 src/utils/startupProfiler.ts — 基于 `perf_hooks` 的性能埋点实现。L38-54 of 195
typescript
// Track memory snapshots separately (perf_hooks doesn't track memory).
// Only used when DETAILED_PROFILING is enabled.
// Stored as an array that appends in the same order as perf.mark() calls, so
// memorySnapshots[i] corresponds to getEntriesByType('mark')[i]. Using a Map
// keyed by checkpoint name is wrong because some checkpoints fire more than
// once (e.g. loadSettingsFromDisk_start fires during init and again after
// plugins reset the settings cache), and the second call would overwrite the
// first's memory snapshot.
const memorySnapshots: NodeJS.MemoryUsage[] = []

// Phase definitions for Statsig logging: [startCheckpoint, endCheckpoint]
const PHASE_DEFINITIONS = {
  import_time: ['cli_entry', 'main_tsx_imports_loaded'],
  init_time: ['init_function_start', 'init_function_end'],
  settings_time: ['eagerLoadSettings_start', 'eagerLoadSettings_end'],
  total_time: ['cli_entry', 'main_after_run'],
} as const
  • src/utils/log.ts: API 请求捕获与错误下沉(ErrorLogSink)。
📄 src/utils/log.ts — API 请求捕获与错误下沉(ErrorLogSink)。L8-11 of 363
typescript
  setLastAPIRequest,
  setLastAPIRequestMessages,
} from '../bootstrap/state.js'
import { TICK_TAG } from '../constants/xml.js'

基于 Claude Code v2.1.88 开源快照的深度分析