Skip to main content

排查工具链

这一份是「在排查之前先看一遍」,搞清楚每个工具擅长什么、不擅长什么,避免一上来就 console.log 走天下。

4.1 工具一览

工具类型擅长不擅长
Trae IDE 内置 Node DebugIDE 自带断点、调用栈、Scope、闭包、Console、Watch、内存面板多人协作 / 远程 attach
node --inspect + Chrome DevTools内置同上,外加 Memory / Profiler 完整面板要切浏览器
Chrome DevTools Memory内置堆快照对比、Retainers 链性能火焰图
clinic.js doctor第三方CPU 热点、事件循环延迟精细内存分析
clinic.js heap第三方内存增长曲线 + 自动标可疑对象CPU
heapdump第三方手动导出 .heapsnapshot不分析,只导出
node --prof + node --prof-process内置函数级耗时统计可读性差
perf_hooks.monitorEventLoopDelay内置实时事件循环 lag历史回溯
async_hooks内置追踪异步上下文开销大,主要用于框架

4.1.1 优先用 Trae IDE 内置 Debug(最省事)

现象:用 pnpm dev(或带 --inspect 的任何方式)启动后,IDE 的 Debug / 运行控制台面板会自动 attach 一个 "DevTools for Node" 视图——不需要手动开浏览器访问 chrome://inspect

能做什么

  • 断点:在源码行号左侧点一下就行(断点会持久化在 .vscode/launch.json 或 IDE 自己的配置里)
  • 暂停 / 单步 / Step Into / Step Out
  • 左侧 Variables / Scope 看闭包变量、模块级变量、this
  • 顶部 Call Stack 看完整调用链(包括异步栈)
  • Console 面板可以直接 process.memoryUsage()process._getActiveHandles() 之类的 REPL
  • Memory 面板可以手动 Take Snapshot、做 Comparison

典型用法

  1. pnpm dev(默认就带 --inspect=0.0.0.0:9229在哪跑都行)→ Trae IDE 自动 attach,控制台出现 "Debugger attached"。
  2. 在 IDE 里打开 server/src/handlers/debug.js 的某个 handler,在 throw new Error(...) 那行打个断点。
  3. 浏览器 / curl 触发对应接口 → 自动断在断点处。
  4. 看 Scope 里的 reqresctx、闭包捕获的变量。

脚本说明package.json):

  • pnpm dev = node --inspect=0.0.0.0:9229 src/index.js(默认就开 inspector)
  • pnpm dev:inspect = 同 dev(保留为语义化别名)
  • pnpm dev:no-debug = 纯运行,不开 inspector(想对比「调试 vs 不调试」开销时用)
  • pnpm dev:inspect-brk = 入口第一行就断下来(强制定位到启动过程)

踩坑提醒

  • 改了代码后 Node 不会自动 reload(不像前端 HMR)。需要 Ctrl+C 重启。
  • 断点如果没生效,多半是路径映射问题——确认 IDE 打开的就是 server/src/handlers/... 这份源码(不是被 sourcemap 转译过的)。
  • 想强制从入口第一行断下来:用 pnpm dev:inspect-brk

4.2 推荐使用顺序

场景 A:CPU 高 / 接口慢

  1. clinic doctor 一键出火焰图 → 找热点函数。
  2. 如果还不够清楚 → Chrome DevTools Profiler 录制几秒。
  3. 想看每行耗时 → node --prof + node --prof-process

场景 B:内存泄漏

  1. 先用 process.memoryUsage() 观察曲线(/memory/heap-stats)。
  2. 拿不准是 Buffer 还是 JS 堆 → 看 external / arrayBuffers 是不是暴涨。
  3. clinic heap,它会自动采样。
  4. 还是看不出 → POST /memory/heap-snapshot.heapsnapshot,丢进 DevTools Memory 面板。
  5. 关键步骤:对比两次快照,看 Retained Size 增长的对象。

场景 C:偶发崩溃

  1. node --inspect + DevTools → 勾上 Pause on Exceptions
  2. 复现一次,等自动断下来。
  3. 看 Call Stack 上一层一层找根因。
  4. 如果是异步栈丢失 → 用 --async-stack-traces (默认开启)。

场景 D:进程不退

  1. process._getActiveHandles() 列出来一个个看。
  2. Heap Snapshot 找 Timeout / Socket / Server
  3. 90% 是没 clear 的定时器或监听器。

4.3 常用命令

# 开启 inspect 模式
node --inspect=0.0.0.0:9229 src/index.js

# 只在第一行断点(断在入口)
node --inspect-brk=0.0.0.0:9229 src/index.js

# 配合 clinic(要先 npm i -g clinic)
clinic doctor -- node src/index.js
clinic heap -- node src/index.js
clinic flame -- node src/index.js

# V8 prof
node --prof src/index.js
# 跑一会儿后 Ctrl+C,会生成 isolate-*.log
node --prof-process isolate-*.log > profile.txt

# 手动堆快照
curl -X POST localhost:3000/memory/heap-snapshot
ls *.heapsnapshot

4.4 在本项目里怎么用

cd node补强/server

# 1. 默认模式(已带 --inspect=0.0.0.0:9229,IDE 会自动 attach)
pnpm dev

# 2. 想强制从入口第一行断下来
pnpm dev:inspect-brk

# 3. 想对比「不调试」 vs 「调试」开销
pnpm dev:no-debug

# 4. Chrome DevTools 协查
# 浏览器打开 chrome://inspect,点 "inspect" 进入 DevTools

3. clinic 模式

pnpm dev:clinic-heap # 或 clinic-doctor / clinic-flame

跑一段压力测试后 Ctrl+C,会在 server/.clinic/ 下生成 report

报告是 .heapprofiler.html 形式,浏览器直接打开

4.5 工具选型原则

  • 先观察,再下钻:不要一上来就 attach inspector,先用 process.memoryUsage()monitorEventLoopDelay 摸个底。
  • 隔离环境复现:本服务就是为了「制造可控现场」,不要去生产环境直接 attach。
  • 别迷信工具console.log 在很多场景下依然是最快的方式——尤其是想看「某个值在第几次循环后变了」。
  • 保留现场:如果进程马上要崩,第一件事是 kill -SIGUSR2 <pid>(heapdump 监听此信号)或者发个 heap snapshot 请求,比抢救日志更重要。