调试子系统 (Debug Subsystem)
在 Claude Code 这样复杂的 CLI 工具中,当模型“发疯”或者工具调用失败时,仅仅看终端输出是不够的。Claude Code 内置了一套极其详尽的调试子系统,它记录了模型的所有 API 往返,同时涵盖启动性能分析和针对开发者的分类过滤功能。
先搞清楚这是什么
它是一个分层日志与遥测系统。它将运行时状态分为不同级别(verbose 到 error),并提供了多种非侵入式的观测手段。对于 Anthropic 内部员工(Ants),它是一个自动开启的“飞行记录仪”;对于外部用户,它是一个可随时唤醒的故障诊断助手。
代码里的真实逻辑
五级日志体系: 在
src/utils/debug.ts中定义了verbose,debug,info,warn,error五种级别。默认最小级别是debug,只有设置CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose才会记录高频的底层信息(如 shell 命令的完整 stdout/stderr)。激活模式与动态开关: 你可以通过
--debug标志或DEBUG=1环境变量在启动时开启。最精妙的是/debug斜杠命令(由src/skills/bundled/debug.ts实现),它能在会话中途动态开启调试模式。即使你启动时没带参数,发现不对劲时输入/debug,它会立即激活BufferedWriter开始记录后续操作。模式过滤 (Pattern Filtering):
src/utils/debugFilter.ts提供了一种类似DEBUG环境变量但更强大的语法:--debug=api,hooks:只看 API 调用和生命周期钩子。--debug=!mcp,!file:排除 MCP 和文件操作相关的噪音。 系统会自动从日志消息前缀(如[API]或MCP server "name":)中提取类别进行匹配。
智能写入策略:
- Ants 模式:内部员工默认开启日志,使用
BufferedWriter以 1 秒为间隔异步刷新到磁盘,确保不影响交互性能。 - Debug 模式:通过参数开启时,切换为同步写入(
appendFileSync),防止程序异常退出时丢失关键证据。 - 软链接:每次会话都会在
~/.claude/debug/下生成唯一 ID 的日志文件,并自动更新一个latest软链接,方便开发者直接tail -f ~/.claude/debug/latest。
- Ants 模式:内部员工默认开启日志,使用
启动性能分析 (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 — 核心调试逻辑、日志路径管理、写入器实现。
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 过滤算法。
export type DebugFilter = {
include: string[]
exclude: string[]
isExclusive: boolean
}src/utils/startupProfiler.ts: 基于perf_hooks的性能埋点实现。
📄 src/utils/startupProfiler.ts — 基于 `perf_hooks` 的性能埋点实现。
// 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 constsrc/utils/log.ts: API 请求捕获与错误下沉(ErrorLogSink)。
📄 src/utils/log.ts — API 请求捕获与错误下沉(ErrorLogSink)。
setLastAPIRequest,
setLastAPIRequestMessages,
} from '../bootstrap/state.js'
import { TICK_TAG } from '../constants/xml.js'