如何使用多种工具调试 Node.js 代码

已发表: 2022-03-15

Node.js 是一个 JavaScript 运行时,基于与 Google Chrome 浏览器中使用的相同的 V8 引擎。 它通常用于构建跨平台的服务器端和终端应用程序。 Node.js 在过去十年中变得越来越流行,因为它易于安装、实用、速度快,并且允许客户端 Web 开发人员在其他地方利用他们的技能。

但是,软件开发仍然是一项复杂的任务,您的 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 代码中的 ESlint
VS 代码中的 ESlint。

逻辑错误

您的代码运行但未按预期工作。 例如,用户在请求时没有注销; 报告显示不正确的数字; 数据未完全保存到数据库中; 等等

逻辑错误可能由以下原因引起:

  • 使用错误的变量
  • 不正确的条件,例如if (a > 5)而不是if (a < 5)
  • 未能考虑运算符优先级的计算,例如1+2*3的结果是 7 而不是 9。

这是不可避免的:您的 Node.js 代码会在某个时候失败。 在此处查看您可以使用哪些工具来调试应用程序并找到问题的原因Click to Tweet

运行时(或执行)错误

错误只有在应用程序执行时才会变得明显,这通常会导致崩溃。 运行时错误可能由以下原因引起:

  • 除以已设置为零的变量
  • 试图访问不存在的数组项
  • 尝试写入只读文件

尽管以下开发技术可以提供帮助,但逻辑和运行时错误更难发现:

  1. 使用测试驱动开发: TTD 鼓励您在开发函数之前编写测试,例如,当 Z 作为参数传递时,X 从函数 Y 返回。 这些测试在初始开发和后续更新期间运行,以确保代码继续按预期工作。
  2. 使用问题跟踪系统:没有什么比声称“您的软件无法运行”的电子邮件更糟糕的了! 问题跟踪系统允许您记录特定问题、记录复制步骤、确定优先级、分配开发人员并跟踪修复进度。
  3. 使用源代码控制: 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 条件,例如

// running in development mode? const devMode = (process.env.NODE_ENV !== 'production'); if (devMode) { console.log('application is running in development mode'); }

您还可以使用 Node 的 util.debuglog 方法有条件地输出错误消息,例如

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()还接受逗号分隔值的列表:

 let x = 123; console.log('x:', x); // x: 123

…尽管 ES6 解构提供了类似的输出,但花费更少:

 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> 提示:

 $ 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 or c : 继续执行
  • nextn :运行下一个命令
  • steps :进入被调用的函数
  • out or o : 跳出函数并返回调用语句
  • pause :暂停正在运行的代码
  • watch('myvar') : 观察一个变量
  • setBreakPoint()sb() :设置断点
  • restart :重新启动脚本
  • .exitCtrl | 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以查看本地和联网设备:

铬检查工具
Chrome 检查工具。

如果您的 Node.js 应用程序未显示为Remote Target ,则:

  • 单击 Open dedicated DevTools for Node 并选择地址和端口,或者
  • 检查Discover network targets ,单击Configure ,然后添加运行它的设备的 IP 地址和端口。

单击 Target 的检查链接以启动 DevTools 调试器客户端。 使用 DevTools 进行客户端代码调试的任何人都应该熟悉这一点:

Chrome 开发者工具
Chrome 开发工具。

切换到面板。 您可以通过点击 Cmd | 打开任何文件Ctrl + P 并输入其文件名(例如 index.js)。

但是,将项目文件夹添加到工作区更容易。 这允许您直接从 DevTools 加载、编辑和保存文件(您是否认为这是一个好主意是另一回事!)

  1. 单击+ 将文件夹添加到工作区
  2. 选择 Node.js 项目的位置
  3. 点击同意以允许文件更改

您现在可以从左侧目录树加载文件:

Chrome DevTools 源面板
Chrome DevTools 源面板。

单击任何行号以设置由蓝色标记表示的断点。

调试基于断点。 这些指定调试器应在何处暂停程序执行并显示程序的当前状态(变量、调用堆栈等)

您可以在用户界面中定义任意数量的断点。 另一种选择是放置一个调试器; 声明到您的代码中,当附加调试器时停止。

加载并使用您的 Web 应用程序到达设置断点的语句。 在此处的示例中,http://localhost:3000/ 在任何浏览器中打开,DevTools 将在第 44 行停止执行:

铬断点
铬断点。

右侧面板显示:

因停机时间和 WordPress 问题而苦苦挣扎? Kinsta 是旨在节省您时间的托管解决方案! 查看我们的功能
  • 一排操作图标(见下文)。
  • 监视窗格允许您通过单击+图标并输入变量名称来监视变量。
  • Breakpoints窗格显示所有断点的列表,并允许启用或禁用它们。
  • Scope窗格显示所有本地、模块和全局变量的状态。 您将最常检查此窗格。
  • 调用堆栈窗格显示了为达到这一点而调用的函数的层次结构。

Paused on breakpoint上方显示了一排操作图标:

Chrome 断点图标
Chrome 断点图标。

从左到右,它们执行以下操作:

  • resume execution : 继续处理直到下一个断点
  • step over :执行下一个命令,但停留在当前代码块内——不要跳转到它调用的任何函数
  • step into : 执行下一个命令并根据需要跳转到任何函数
  • step out :继续处理到函数结束,返回调用命令
  • step : 与step into类似,只是它不会跳转到异步函数
  • 停用所有断点
  • pause on exceptions :发生错误时停止处理。

条件断点

有时有必要对断点进行更多控制。 假设您有一个完成 1,000 次迭代的循环,但您只对最后一次的状态感兴趣:

 for (let i = 0; i < 1000; i++) { // set breakpoint here }

您可以右键单击该行,选择Add conditional breakpoint ,然后输入条件,例如i = 999 ,而不是单击resume execution 999 次:

Chrome 条件断点
Chrome 条件断点。

Chrome 以黄色而不是蓝色显示条件断点。 在这种情况下,断点仅在循环的最后一次迭代时触发。

日志点

日志点无需任何代码即可有效实现 console.log()! 当代码执行任何一行时,可以输出一个表达式,但它不会停止处理,这与断点不同。

要添加日志点,请右键单击任意行,选择Add log point ,然后输入表达式,例如'loop counter i', i

Chrome 日志点
Chrome 日志点。

在上面的示例中,DevTools 控制台将loop counter i: 0输出到loop counter i: 999

使用 VS Code 调试 Node.js 应用程序

VS Code 或 Visual Studio Code 是 Microsoft 提供的免费代码编辑器,在 Web 开发人员中很受欢迎。 该应用程序可用于 Windows、macOS 和 Linux,并使用 Electron 框架中的 Web 技术开发。

VS Code 支持 Node.js 并具有内置的调试客户端。 大多数应用程序无需任何配置即可调试; 编辑器会自动启动调试服务器和客户端。

打开启动文件(例如 index.js),激活Run and Debug窗格,单击Run and Debug按钮,然后选择Node.js环境。 单击任意行以激活显示为红色圆圈图标的断点。 然后,像以前一样在浏览器中打开应用程序——VS Code 在到达断点时停止执行:

VS 代码断点
VS 代码断点。

VariablesWatchCall StackBreakpoints窗格与 Chrome DevTools 中显示的相似。 Loaded Scripts窗格显示已加载的脚本,尽管许多脚本是 Node.js 内部的。

操作图标工具栏允许您:

  • resume execution : 继续处理直到下一个断点
  • step over :执行下一个命令,但停留在当前函数内——不要跳转到它调用的任何函数
  • step into :执行下一个命令并跳转到它调用的任何函数
  • step out :继续处理到函数结束,返回调用命令
  • 重新启动应用程序和调试器
  • 停止应用程序和调试器

与 Chrome DevTools 一样,您可以右键单击任意行以添加Conditional breakpointsLog points

有关详细信息,请参阅 Visual Studio Code 中的调试。

VS Code 高级调试配置

如果您想在另一台设备、虚拟机上调试代码,或者需要使用其他启动选项(例如 nodemon),则可能需要进一步的 VS Code 配置。

VS Code 将调试配置存储在项目中.vscode目录内的 launch.json 文件中。 打开Run and Debug窗格,单击create a launch.json file ,然后选择Node.js环境来生成此文件。 提供了一个示例配置:

VS Code 调试器配置
VS Code 调试器配置。

可以将任意数量的配置设置定义为"configurations"数组中的对象。 单击添加配置...并选择适当的选项。

单个 Node.js 配置可以:

  1. 启动进程本身,或
  2. 附加到调试 Web Socket 服务器,可能在远程机器或 Docker 容器上运行。

例如,要定义 nodemon 配置,请选择Node.js: Nodemon Setup并在必要时更改“程序”入口脚本:

 { // custom configuration "version": "0.2.0", "configurations": [ { "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", "name": "nodemon", "program": "${workspaceFolder}/index.js", "request": "launch", "restart": true, "runtimeExecutable": "nodemon", "skipFiles": [ "<node_internals>/**" ], "type": "pwa-node" } ] }

保存launch.json文件, nodemon (配置“名称”)出现在Run and Debug窗格顶部的下拉列表中。 单击绿色运行图标开始使用该配置并使用 nodemon 启动应用程序:

使用 nodemon 进行 VS 代码调试
使用 nodemon 进行 VS Code 调试。

和以前一样,您可以添加断点、条件断点和日志点。 主要区别在于,当文件被修改时,nodemon 会自动重启你的服务器。

有关详细信息,请参阅 VS Code 启动配置。

以下 VS Code 扩展还可以帮助您调试托管在远程或隔离服务器环境中的代码:

  • 远程——容器:连接到在 Docker 容器中运行的应用程序
  • 远程——SSH:连接到远程服务器上运行的应用程序
  • 远程 - WSL:连接到在 Linux 的 Windows 子系统 (WSL) 上运行的应用程序。

其他 Node.js 调试选项

Node.js 调试指南为一系列文本编辑器和 IDE 提供建议,包括 Visual Studio、JetBrains WebStorm、Gitpod 和 Eclipse。 Atom 提供了一个节点调试扩展,它将 Chrome DevTools 调试器集成到编辑器中。

一旦您的应用程序上线,您可以考虑使用商业调试服务,例如 LogRocket 和 Sentry.io,它们可以记录和回放真实用户遇到的客户端和服务器错误。

需要一些帮助调试应用程序? 从这里开始点击鸣叫

概括

从历史上看,JavaScript 调试一直很困难,但在过去十年中已经有了巨大的改进。 选择与为其他语言提供的选择一样好——如果不是更好的话。

使用任何实用的工具来定位问题。 console.log() 用于快速查找 bug 没有任何问题,但对于更复杂的问题,Chrome DevTools 或 VS Code 可能更可取。 这些工具可以帮助您创建更强大的代码,并且您将花费更少的时间来修复错误。

你发誓的 Node.js 调试实践是什么? 在下面的评论部分分享!