Node.js是一个JavaScript运行时,基于与Google Chrome浏览器中使用的相同的V8引擎。它通常用于构建跨平台的服务器端和终端应用程序。Node.js在过去十年中变得越来越流行,因为它易于安装、实用、速度快,并且允许客户端Web开发人员在其他地方利用他们的技能。
但是,软件开发仍然是一项复杂的任务,您的Node.js代码有时会失败。本教程演示了各种工具来帮助调试应用程序并找出问题的原因。
Tips:Deno是一个替代的JavaScript运行时。它与Node.js相似,但更新,它消除了一些裂缝和不一致。下面的工具和信息通常可以应用于Deno应用程序和Node.js。
- 调试概述
- 设置适当的Node.js环境变量
- 使用Node.js命令行选项
- 将消息输出到控制台
- 使用第三方日志系统
- 使用V8检查器
- 使用Chrome浏览器调试Node.js代码
- 条件断点
- 日志点
- 使用VS Code调试Node.js应用程序
- VS Code高级调试配置
- 其他Node.js调试选项
调试概述
“调试”是修复软件缺陷的各种方法的名称。修复错误通常很简单。查找错误的原因可能要复杂得多,并且需要花费数小时的时间。
以下部分描述了您将遇到的三种一般类型的错误。
语法错误
您的代码不遵循语言规则——例如,当您省略右括号或拼写错误的语句,如console.lag(x)
.
一个好的代码编辑器可以通过以下方式帮助发现常见问题:
- 对有效或无效语句进行颜色编码
- 类型检查变量
- 自动完成函数和变量名
- 突出显示匹配的括号
- 自动缩进代码块
- 检测无法访问的代码
- 重构杂乱的功能
VS Code和Atom等免费编辑器对Node.js、JavaScript和TypeScript(可转换为JavaScript)提供了很好的支持。在保存和测试代码之前,通常可以发现基本语法问题。
像ESLint这样的代码linter也会报告语法错误、缩进错误和未声明的变量。ESLint是一个Node.js工具,您可以通过以下方式全局安装:
npm i eslint -g
您可以使用以下命令从命令行检查JavaScript文件:
eslint mycode.js
…但是使用编辑器插件更容易,例如用于VS Code的ESLint或用于Atom的 linter-eslint,它们会在您键入时自动验证代码:
VS Code中的ESlint
逻辑错误
您的代码运行但未按预期工作。例如,用户在请求时没有注销;报告显示不正确的数字;数据未完全保存到数据库中;等等。
逻辑错误可能由以下原因引起:
- 使用错误的变量
- 不正确的条件,例如,
if (a > 5)
而不是if (a < 5)
- 未能考虑运算符优先级的计算,例如,
1+2*3
结果为7而不是9。
运行时(或执行)错误
错误只有在应用程序执行时才会变得明显,这通常会导致崩溃。运行时错误可能由以下原因引起:
- 除以已设置为零的变量
- 试图访问不存在的数组项
- 尝试写入只读文件
尽管以下开发技术可以提供帮助,但逻辑和运行时错误更难发现:
- 使用测试驱动开发: TTD鼓励您在开发函数之前编写测试,例如,当Z作为参数传递时,X从函数Y返回。这些测试在初始开发和后续更新期间运行,以确保代码继续按预期工作。
- 使用问题跟踪系统:没有什么比声称“您的软件无法运行”的电子邮件更糟糕的了!问题跟踪系统允许您记录特定问题、记录复制步骤、确定优先级、分配开发人员并跟踪修复进度。
- 使用源代码控制:Git之类的源代码控制系统将帮助您备份代码、管理修订并确定引入错误的位置。在线存储库,包括Github和Bitbucket,为小型或开源项目提供免费空间和工具。
您仍然会遇到Node.js错误,但以下部分描述了定位该难以捉摸的错误的方法。
设置适当的Node.js环境变量
在主机操作系统中设置的环境变量可以控制Node.js应用程序和模块设置。最常见的是NODE_ENV
,通常在调试时设置为开发,或者在实时服务器上运行时设置为生产。在macOS或Linux上设置环境变量:
NODE_ENV=development
或在(经典)Windows命令提示符下:
set NODE_ENV=development
或Windows Powershell:
$env:NODE_ENV="development"
在流行的Express.js框架中,将NODE_ENV设置为development会禁用模板文件缓存并输出详细的错误消息,这在调试时可能会有所帮助。其他模块可能提供类似的功能,您可以在应用程序中添加NODE_ENV条件,例如
const devMode = (process.env.NODE_ENV !== ‘production’);
if (devMode) {
console.log(‘application is running in development mode’);
}
// running in development mode? const devMode = (process.env.NODE_ENV !== 'production'); if (devMode) { console.log('application is running in development mode'); }
您还可以使用Node的util.debuglog方法有条件地输出错误消息,例如
const myappDebug = debuglog(‘myapp’);
myappDebug(‘log something’);
import { debuglog } from 'util'; const myappDebug = debuglog('myapp'); myappDebug('log something');
此应用程序仅在NODE_DEBUG设置为 myapp 或 * 或 my* 等通配符时才会输出日志消息。
使用Node.js命令行选项
节点脚本通常使用node后跟入口脚本的名称来启动:
node app.js
您还可以设置命令行选项来控制各种运行时方面。用于调试的有用标志包括:
--check
语法检查脚本而不执行--trace-warnings
当JavaScript Promises未解析或拒绝时输出堆栈跟踪--enable-source-maps
使用TypeScript等转译器时显示源映射--throw-deprecation
使用已弃用的Node.js功能时发出警告--redirect-warnings=file
将警告输出到文件而不是stderr--trace-exit
调用时输出堆栈跟踪process.exit()
。
将消息输出到控制台
输出控制台消息是调试Node.js应用程序的最简单方法之一:
console.log(`someVariable: ${ someVariable }`);
很少有开发人员意识到还有许多其他控制台方法:
控制台方法 | 描述 |
---|---|
.log(msg) |
标准控制台消息 |
.log('%j', obj) |
将对象输出为紧凑的JSON字符串 |
.dir(obj, opt) |
漂亮打印对象属性 |
.table(obj) |
以表格格式输出数组和对象 |
.error(msg) |
错误信息 |
.count(label) |
增加一个命名的计数器和输出 |
.countReset(label) |
重置命名计数器 |
.group(label) |
缩进一组消息 |
.groupEnd(label) |
终止一个组 |
.time(label) |
启动一个命名的计时器 |
.timeLog(label) |
报告经过的时间 |
.timeEnd(label) |
停止一个命名的计时器 |
.trace() |
输出堆栈跟踪(所有函数调用的列表) |
.clear() |
清除控制台 |
console.log()
还接受逗号分隔值的列表:
console.log(‘x:’, x);
// x: 123
let x = 123; console.log('x:', x); // x: 123
…尽管ES6解构提供了类似的输出,但花费更少:
// { x: 123 }
console.log({ x }); // { x: 123 }
console.dir()命令以与util.inspect()相同的方式漂亮地打印对象属性:
console.dir(myObject, { depth: null, color: true });
控制台争议
一些开发人员声称您永远不应该使用console.log()
,因为:
- 您正在更改代码并且可能会更改某些内容或忘记删除它,并且
- 当有更好的调试选项时,就没有必要了。
不要相信任何声称他们从未使用过的人console.log()
!记录既快又脏,但每个人都在某个时候使用它。使用您喜欢的任何工具或技术。修复一个错误比你找到它的方法更重要。
使用第三方日志系统
第三方日志系统提供更复杂的功能,例如消息传递级别、详细程度、排序、文件输出、分析、报告等。流行的解决方案包括cabinet、loglevel、morgan、pino、signale、storyboard、tracer和winston。
使用V8检查器
V8 JavaScript引擎提供了一个可以在Node.js中使用的调试客户端。使用节点检查启动应用程序,例如
node inspect app.js
调试器在第一行暂停并显示debug>提示:
< Debugger listening on ws://127.0.0.1:9229/143e23fb
< For help, see: https://nodejs.org/en/docs/inspector
<
ok
< Debugger attached.
<
Break on start in mycode.js:1
> 1 const count = 10;
2
3 for (i = 0; i < counter; i++) {
debug>
$ node inspect .mycode.js < Debugger listening on ws://127.0.0.1:9229/143e23fb < For help, see: https://nodejs.org/en/docs/inspector < ok < Debugger attached. < Break on start in mycode.js:1 > 1 const count = 10; 2 3 for (i = 0; i < counter; i++) { debug>
输入帮助以查看命令列表。您可以通过输入以下内容逐步完成应用程序:
- cont 或 c : 继续执行
- next 或 n:运行下一个命令
- step 或 s:进入被调用的函数
- out 或 o : 跳出函数并返回调用语句
- pause:暂停正在运行的代码
- watch(‘myvar’) : 观察一个变量
- setBreakPoint() 或 sb():设置断点
- restart:重新启动脚本
- .exit 或 Ctrl | Cmd + D:退出调试器
诚然,这种调试选项既费时又笨拙。仅在没有其他选项时使用它,例如当您在远程服务器上运行代码并且无法从其他地方连接或安装其他软件时。
使用Chrome浏览器调试Node.js代码
上面使用的Node.js检查选项启动了一个Web Socket服务器,它在localhost端口9229上进行侦听。它还启动了一个基于文本的调试客户端,但也可以使用图形客户端——例如内置于Google Chrome和基于Chrome的客户端Chromium、Edge、Opera、Vivaldi和Brave等浏览器。
要调试典型的Web应用程序,请使用–inspect选项启动它以启用V8调试器的Web Socket服务器:
node --inspect index.js
笔记:
- index.js被假定为应用程序的入口脚本。
- 确保您使用
--inspect
双破折号以确保您不会启动基于文本的调试器客户端。 - 如果您想在文件更改时自动重新启动应用程序,您可以使用nodemon而不是node。
默认情况下,调试器只接受来自本地机器的传入连接。如果您在其他设备、虚拟机或Docker容器上运行应用程序,请使用:
node --inspect=0.0.0.0:9229 index.js
节点检查选项
您还可以使用--inspect-brk
而不是--inspect
在第一行停止处理(设置断点),以便您可以从头开始逐步执行代码。
打开基于Chrome的浏览器并chrome://inspect
在地址栏中输入以查看本地和联网设备: