排查工具链
这一份是「在排查之前先看一遍」,搞清楚每个工具擅长什么、不擅长什么,避免一上来就
console.log走天下。
4.1 工具一览
| 工具 | 类型 | 擅长 | 不擅长 |
|---|---|---|---|
| Trae IDE 内置 Node Debug | IDE 自带 | 断点、调用栈、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
典型用法:
- 跑
pnpm dev(默认就带--inspect=0.0.0.0:9229,在哪跑都行)→ Trae IDE 自动 attach,控制台出现 "Debugger attached"。 - 在 IDE 里打开 server/src/handlers/debug.js 的某个 handler,在
throw new Error(...)那行打个断点。 - 浏览器 / curl 触发对应接口 → 自动断在断点处。
- 看 Scope 里的
req、res、ctx、闭包捕获的变量。
脚本说明(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 高 / 接口慢
clinic doctor一键出火焰图 → 找热点函数。- 如果还不够清楚 → Chrome DevTools Profiler 录制几秒。
- 想看每行耗时 →
node --prof+node --prof-process。
场景 B:内存泄漏
- 先用
process.memoryUsage()观察曲线(/memory/heap-stats)。 - 拿不准是 Buffer 还是 JS 堆 → 看
external/arrayBuffers是不是暴涨。 - 跑
clinic heap,它会自动采样。 - 还是看不出 →
POST /memory/heap-snapshot拿.heapsnapshot,丢进 DevTools Memory 面板。 - 关键步骤:对比两次快照,看 Retained Size 增长的对象。
场景 C:偶发崩溃
node --inspect+ DevTools → 勾上 Pause on Exceptions。- 复现一次,等自动断下来。
- 看 Call Stack 上一层一层找根因。
- 如果是异步栈丢失 → 用
--async-stack-traces(默认开启)。
场景 D:进程不退
process._getActiveHandles()列出来一个个看。- Heap Snapshot 找
Timeout/Socket/Server。 - 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 请求,比抢救日志更重要。