这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战
Node.js 提供 Inspector Protocol 调试协议来支持调试。
简单来说,在运行 Node.js 程序时开启一个进程来监听调试请求,默认的监听端口是 9229。每个监听进程都被分配了唯一的 UUID,调试客户端通过 ws://{host}:{port}/{UUID} 和监听进程建立 websocket 通信。
Node.js 开启调试服务被动等待,调试客户端主动发起对接。
Inspector 更确切的称呼是 “检查器”,由于理解习惯,文章内将其称为 “调试器”
调试服务(Inspector agent)
启动时开启调试服务
Node.js 调试服务默认关闭,需要通过 --inspect 或 --inspect-brk 标识符开启,服务的默认端口和地址也可通过 node --inspect=[host:port] 指定。
node --inspect index.js开启调试服务,端口为 9229node --inspect=3003 index.js开启调试服务,端口为 3003node --inspect=192.168.0.101开启调试服务,地址为 192.168.0.101:9229node --inspect=192.168.0.101:3003开启调试服务,连接地址为 192.168.0.101:3003
那么,--inspect 和 --inspect-brk 有什么不同?
--inspect-brk会在代码执行前停住。
如下图
--inspect启动调试服务,跑完程序就退出。除非是有异步任务在,不然完全不给调试器对接的机会。--inspect-brk启动完调试服务就停在开头,等着调试器接入,接入后断在第一行代码等待下一步操作。
对于 node 命令之外的启动脚本,例如 npm/yarn/vercel/next。--inspect 是 Node.js 的标识符,其他脚本无法识别,这种情况可以设置变量 NODE_OPTIONS 来解决。
NODE_OPTIONS='--inspect' vercel dev
运行时开启调试服务
实际上,除了跟随程序启动,还可以在已运行的程序上开启调试服务
kill -s SIGUSR1 49026
上述命令的作用是给进程 id 是 49026 的进程发送 SIGUSR1 信号,当 Node.js 进程收到 SIGUSR1 时,将启动调试服务。
调试客户端(Inspector client)
Node 内置命令行调试器,通过 node inspect 命令执行,通过输入命令来描述行为不如可视化操作高效,可视化调试器必不可少。
目前主流的 IDE 几乎都已经内置 Node.js 调试客户端,例如前端领域最常用的 VSCode、WebStorm、Chrome DevTool。
Chrome DevTools 会根据地址列表自动检查调试服务启动情况,默认地址有本地的 9229 和 9222 端口。chrome://inspect 面板负责调试管理。
VSCode 提供了多种启动调试的方式。大家耳熟能详的是 launch.json 文件,需要手动配置来启动调试,VSCode 努力把这部分工作自动化,来降低启动调试的门槛。
Launch Configuration
launch.json 虽然传统,但仍是最全面的自定义入口。即使是自动化也需要通过部分的配置项来实现自定义。
VSCode 提供了两种启动模式:
launch启动程序并接上调试器attach调试器接入正在运行的程序
基于这两种模式以及其他配置字段,VSCode 能支持多种调试场景。
启动程序并允许调试
比较常规的操作是 node --inspect 启动程序,launch.json 配置 Node.js: Attach
{
"name": "Attach",
"port": 9229,
"request": "attach",
"skipFiles": [ // 单步调试不会进入,抛出未捕获异常不会断住,手动设置断点仍会起作用
"<node_internals>/**"
],
"type": "pwa-node"
}
其实 launch 模式完全能替代上述两步操作。
场景一:直接运行程序入口文件,如 node --inspect app.js ,设置 program
{
"name": "Launch Program",
"program": "${workspaceFolder}/app.js", // 指定程序入口文件
// "args": ["--listen", "8080"],// 传入参数 node app.js --listen=8080
"request": "launch",
"skipFiles": [
"<node_internals>/**"
],
"type": "pwa-node"
}
场景二:脚本跑起程序,如 npm run dev,设置 runtimeExecutable 和 runtimeArgs
{
"type": "node",
"request": "launch",
"name": "Launch via NPM",
"runtimeExecutable": "npm", // 指定脚本入口,vercel dev --debug -> "vercel",
"runtimeArgs": [ // 指定命令,vercel dev --debug -> ["dev"]
"run-script",
"debug"
],
// "cwd": "${workspaceFolder}/src" // 指定脚本执行目录
// "args": ["--debug"],// 传入命令参数,vercel dev --debug -> ["--debug"]
"port": 9229,
"skipFiles": [
"<node_internals>/**"
]
},
VSCode 提供的 Node.js Configuration 模板:Mocha Test、Electron Main、Gulp Task 都是对两种场景的应用。
调试正在运行的程序
方式一:通过监听地址接入。要求程序调试服务已启动,配置能够持续生效。
{
"type": "node",
"request": "attach",
"name": "Attach to remote",
// "address": "192.168.148.2", // 指定监听地址
"port": 9229 // 指定监听端口
}
方式二:通过 processId 接入,由于能通过 processId 向进程发送 SIGUSR1 信号,因此无需手动启动调试服务。缺陷在于 processId 会变动,该配置无法持续生效。
{
"type": "node",
"request": "attach",
"name": "Attach to Node Process",
"processId": 48078 // 指定进程 ID
}
VSCode 以 Action 的形式为 Attach to Node Process 提供了一个快捷入口。命令面板输入 Attach to Node Procees,下拉菜单会罗列出正在运行的所有 Node 进程。选择其一便可接上调试器。等同于以下配置
{
"name": "Attach to Process",
"type": "node",
"request": "attach",
"processId": "${command:PickProcess}" // 显示选择菜单
}
Attach to Node Process 最大的特点在于“能调试所有正在运行的 Node 进程”,不受运行环境影响。
调试 TypeScript
TS 代码转译为 JS 后才能在 Node.js 环境运行,调试器需要 Source Map 来关联源代码和执行代码。
配置 tsconfig.json,开启 sourcemap 并指定输出目录。
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "out",
"sourceMap": true
}
}
创建 launch.json,VSCode 会根据 tsconfig.json 自动生成配置。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/app.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": [
"${workspaceFolder}/out/**/*.js"
]
}
]
}
preLaunchTask 指定预先执行的任务: tsc 根据 tsconfig.json 转译源文件
outFiles 告知调试器去哪里找执行文件,map 文件默认和执行文件处于同一目录,如有特殊情况需要设置 resolveSourceMapLocations 告知调试器去哪些目录找 map 文件。
Auto Attach
总要编辑 launch.json 多少有些麻烦,如果能在执行脚本时自动启动调试就好了。
这就是 Auto Attach 的作用,然而 Auto Attach 功能仅能作用于在 VSCode 集成终端 启动的进程。
Auto Attach 有四种可选模式,命令面板输入 Toggle Auto Attach 选择菜单中罗列几种模式:
- Always:在内置终端启动的所有进程都会自动接上调试器
- Smart:排除 node_modules 目录下的执行脚本(例如
tsc build)。排除范围可在debug.javascript.autoAttachSmartPattern指定 - Only With Flag:只会对带
--inspect或--inspect-brk启动的进程有效果 - Disable:禁用自动对接功能
Auto Attach 依托于集成终端,因此每次切换后都需要重启终端才能生效。
在命令面板选择将直接作用于 VSCode 全局环境,如要为工作区定制,可以设置 .vscode/settings.json。
JavaScript Debug Termial
或许你不想全局启用 Auto Attach,只想在需要的时候调用一下。
使用 JavaScript Debug Terminal 创建一个新的终端窗口,把 Auto Attach 作用范围限制在这个终端窗口内。这就是 JavaScript Debug Termial,局部的 Auto Attach。
总结
- Node.js 和调试器基于 Inspector Protocol 实现调试,Node.js 提供调试服务,调试器通过 websocket 对接调试服务
- VSCode 提供了多种启动 Node.js 调试的方式
- Auto Attach,针对在 VSCode 集成终端执行的脚本,自动启动调试
- JavaScript Debug Terminal,为单个终端窗口提供 Auto Attach 功能
- Attach to Node Process Action,调试运行中的 Node.js 进程
- Launch Configuration,自定义配置
Reference
Debugging - Getting Started | Node.js (nodejs.org)
code.visualstudio.com/docs/nodejs…
5 Steps to debugging Next.js/Node.js from VSCode or Chrome DevTools - DEV Community