软件开发很复杂,有时您的 Node.js 应用程序会失败。如果你幸运的话,你的代码将会崩溃并显示一个明显的错误信息。如果您不走运,您的应用程序将继续执行,但不会产生您期望的结果。如果你真的不走运,一切都会正常工作,直到第一个用户发现灾难性的磁盘擦除错误。

什么是调试?

调试是修复软件缺陷的黑魔法。修复错误通常很容易——更正的字符或额外的代码行就可以解决问题。找到该错误是另一回事,开发人员可能会花费许多不愉快的时间来试图找到问题的根源。幸运的是,Node.js 有一些很棒的工具可以帮助跟踪错误。

术语

调试有自己选择的晦涩术语,包括以下内容:

学期

解释

breakpoint

调试器停止程序以便检查其状态的点

debugger

一种提供调试工具的工具,例如逐行运行代码以检查内部变量状态

feature

如声明中所述:“这不是错误,而是功能”。所有开发人员在他们职业生涯的某个时候都会这么说

frequency

错误发生的频率或条件

it doesn’t work

最常出现但最没用的错误报告

log point

给调试器的指令以在执行期间的某个点显示变量的值

logging

将运行时信息输出到控制台或文件

logic error

该程序有效,但未按预期运行

priority

在计划更新列表中分配错误的位置

race condition

难以追踪的错误取决于不可控事件的顺序或时间

refactoring

重写代码以提高可读性和维护性

regression

可能由于其他更新而重新出现以前修复的错误

related

与另一个相似或相关的错误

reproduce

导致错误所需的步骤

RTFM error

伪装成错误报告的用户无能,通常随后是对“阅读翻转手册”的响应

step into

在调试器中逐行运行代码时,进入被调用的函数

step out

逐行运行时,完成当前函数的执行并返回调用代码

step over

逐行运行时,在不进入它调用的函数的情况下完成命令的执行

severity

错误对系统的影响。例如,数据丢失通常被认为比 UI 问题更成问题,除非发生频率非常低

stack trace

错误发生前调用的所有函数的历史列表

syntax error

印刷错误,例如console.log()

user error

由用户而非应用程序引起的错误,但仍可能会根据该人的资历进行更新

watch

在调试器执行期间要检查的变量

watchpoint

类似于断点,但程序在变量设置为特定值时停止

如何避免错误

在测试应用程序之前,通常可以防止错误……

使用好的代码编辑器

一个好的代码编辑器将提供许多功能,包括行号、自动完成、颜色编码、括号匹配、格式化、自动缩进、变量重命名、片段重用、对象检查、函数导航、参数提示、重构、无法访问的代码检测,建议、类型检查等。

Node.js 开发者被VS Code、Atom和Brackets等免费编辑器以及大量商业替代品所宠坏了。

使用代码 Linter

在保存和测试代码之前,linter 可以报告代码错误,例如语法错误、缩进不良、未声明的变量和括号不匹配。JavaScript 和 Node.js 的流行选项包括ESLint、JSLint和JSHint。

这些通常作为全局 Node.js 模块安装,因此您可以从命令行运行检查:

eslint myfile.js

但是,大多数 linter 都有代码编辑器插件,例如用于 VS Code的 ESLint和用于 Atom 的 linter-eslint,它们会在您键入时检查您的代码:

使用源代码管理

Git等源代码控制系统可以帮助保护您的代码并管理修订。更容易发现在何时何地引入了错误以及谁应该受到指责!GitHubBitbucket等在线存储库提供免费空间和管理工具。

采用问题跟踪系统

如果没有人知道,是否存在错误?问题跟踪系统用于报告错误、查找重复项、记录复制步骤、确定严重性、计算优先级、分配开发人员、记录讨论以及跟踪任何修复的进度。

在线源存储库通常提供基本的问题跟踪,但专用解决方案可能适用于较大的团队和项目。

使用测试驱动开发

测试驱动开发(TDD) 是一个开发过程,它鼓励开发人员编写代码,在编写函数之前测试函数的操作——例如,当函数 Y 传递输入 Z 时是否返回X。

可以在开发代码时运行测试以证明功能有效,并在进行进一步更改时发现任何问题。也就是说,您的测试也可能有错误……

走开

很容易熬夜以徒劳地试图找到一个讨厌的错误的来源。不。走开,做点别的。你的大脑会下意识地处理这个问题,并在凌晨 4 点用解决方案叫醒你。即使这没有发生,新鲜的眼睛也会发现那个明显丢失的分号

Node.js 调试:环境变量

在主机操作系统中设置的环境变量可用于控制 Node.js 应用程序设置。最常见的是NODE_ENV,通常development在调试时设置为 。

Linux/macOS 上可以设置环境变量

NODE_ENV=development

窗户cmd:

set NODE_ENV=development

Windows Powershell

$env:NODE_ENV="development"

在内部,应用程序将启用进一步的调试功能和消息。例如:

// is NODE_ENV set to "development"?
const DEVMODE = (process.env.NODE_ENV === 'development');

if (DEVMODE) {
  console.log('application started in development mode on port ${PORT}');
}

NODE_DEBUG启用使用 Node.js 调试消息util.debuglog(见下文),但也可以查阅主要模块和框架的文档以发现更多选项。

请注意,环境变量也可以保存到.env文件中。例如:

NODE_ENV=development
NODE_LOG=./log/debug.log
SERVER_PORT=3000
DB_HOST=localhost
DB_NAME=mydatabase

dotenv然后使用模块加载:

require('dotenv').config();

Node.js 调试:命令行选项

启动应用程序时,可以将各种命令行选项传递给运行时。node其中最有用的是–trace-warnings,它输出进程警告(包括弃用)的堆栈跟踪。

可以设置任意数量的选项,包括:

  • –enable-source-maps:启用源地图(实验性)
  • –throw-deprecation:使用不推荐使用的功能时抛出错误
  • –inspect:激活 V8 检查器(见下文)

举个例子,让我们尝试记录加密模块的DEFAULT_ENCODING属性,它在 Node v10 中已被弃用:

const crypto = require('crypto');

Function bar() {
  console.log(crypto.DEFAULT_ENCODING);
}

function foo(){
  bar();
}

foo();

现在使用以下命令运行它:

node index.js

然后我们会看到:

buffer
(node:7405) [DEP0091] DeprecationWarning: crypto.DEFAULT_ENCODING is deprecated.

但是,我们也可以这样做:

node --trace-warnings index.js

这会产生以下结果:

buffer
(node:7502) [DEP0091] DeprecationWarning: crypto.DEFAULT_ENCODING is deprecated.
    at bar (/home/Desktop/index.js:4:22)
    at foo (/home/Desktop/index.js:8:3)
    at Object.<anonymous> (/home/Desktop/index.js:11:1)
    at Module._compile (internal/modules/cjs/loader.js:1151:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1171:10)
    at Module.load (internal/modules/cjs/loader.js:1000:32)
    at Function.Module._load (internal/modules/cjs/loader.js:899:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47

这告诉我们弃用警告来自第 4 行(console.log语句)中的代码,该代码在bar函数运行时执行。该函数在第 8 行bar被函数调用,该函数在我们脚本的第 11 行被调用。foofoo

请注意,同样的选项也可以传递给nodemon。

控制台调试

调试应用程序的最简单方法之一是在执行期间将值输出到控制台:

console.log( myVariable );

很少有开发人员会钻研这个不起眼的调试命令,但他们错过了更多的可能性,包括:

console方法

描述

.log(msg)

向控制台输出消息

.dir(obj,opt)

用于util.inspect漂亮地打印对象和属性

.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.log( JSON.stringify(obj) );

util.inspect将格式化对象以便于阅读,但console.dir()会为您完成艰苦的工作。

节点.jsutil.Debuglog

Node.jsutil模块提供了一个内置debuglog方法,可以有条件地将消息写入STDERR

const util = require('util');
const debuglog = util.debuglog('myapp');

debuglog('myapp debug message [%d]', 123);

当NODE_DEBUG环境变量设置为myapp(或通配符,如*or my*)时,控制台中会显示消息:

NODE_DEBUG=myapp node index.js
MYAPP 9876: myapp debug message [123]

这里9876是 Node.js 进程 ID。

默认情况下,util.debuglog是静音的。如果您在不设置变量的情况下运行上述脚本NODE_DEBUG,则不会向控制台输出任何内容。这使您可以在代码中留下有用的调试日志,而不会弄乱控制台以供常规使用。

使用日志模块进行调试

如果您需要更复杂的消息级别、详细程度、排序、文件输出、分析等选项,则可以使用第三方日志记录模块。热门选项包括:

  • 日志级别
  • 摩根(Express.js 中间件)
  • 皮诺
  • 信号
  • 故事板
  • 示踪剂
  • 温斯顿

Node.js V8 检查器

在以下部分中,将使用其他教程中开发的pagehit 项目来说明调试概念。您可以通过以下方式下载它:

git clone https://github.com/sitepoint-editors/pagehit-ram

或者您可以使用任何您自己的代码。

Node.js 是 V8 JavaScript 引擎的包装器,其中包括自己的检查器和调试客户端。首先,使用inspect参数(不要与 混淆–inspect)来启动应用程序:

node inspect ./index.js

调试器将在第一行暂停并显示debug>提示:

< Debugger listening on ws://127.0.0.1:9229/6f38abc1-8568-4035-a5d2-dee6cbbf7e44
< For help, see: https://nodejs.org/en/docs/inspector
< Debugger attached.
Break on start in index.js:7
  5 const
  6   // default HTTP port
> 7   port = 3000,
  8
  9   // Node.js modules
debug>

您可以通过输入以下内容逐步完成应用程序:

  • contc:继续执行
  • nextn:运行下一个命令
  • stepor s: 进入一个被调用的函数
  • outo:跳出函数并返回调用命令
  • pause: 暂停运行代码

其他选项包括:

  • 观察变量值watch('myvar')
  • setBreakpoint()使用/sb()命令设置断点(debugger;在代码中插入语句通常更容易)
  • restart一个脚本
  • .exit调试器(.需要初始值)

如果这听起来非常笨拙,那就是. 仅在绝对没有其他选项时使用内置调试客户端,您感觉特别受虐,并且您没有使用 Windows(这通常是有问题的)。

使用 Chrome 进行 Node.js 调试

Node.js 检查器(没有调试器客户端)以以下–inspect标志启动:

node --inspect ./index.js

注意:必要时nodemon可以代替。node

这将启动调试器侦听127.0.0.1:9229,任何本地调试客户端都可以附加到:

Debugger listening on ws://127.0.0.1:9229/20ac75ae-90c5-4db6-af6b-d9d74592572f

如果您在其他设备或 Docker 容器上运行 Node.js 应用程序,请确保端口9229可访问并使用以下命令授予远程访问权限:

node --inspect=0.0.0.0:9229 ./index.js

或者,您可以使用–inspect-brk在第一条语句上设置断点,以便立即暂停应用程序。

打开 Chrome 并chrome://inspect在地址栏中输入。

注意:如果 Node.js 应用程序未显示为Remote Target,请确保选中Discover network targets ,然后单击配置以添加运行应用程序的设备的 IP 地址和端口。

单击目标的检查链接以启动 DevTools。任何有浏览器调试经验的人都会立即熟悉它。

+ 添加文件夹到工作区链接允许您选择 Node.js 文件在系统上的位置,因此加载其他模块和进行更改变得更加容易。

单击任何行号会设置一个断点,用绿色标记表示,当到达该代码时停止执行:

通过单击+图标并输入变量名称,可以将变量添加到右侧的Watch窗格。只要暂停执行,就会显示它们的值。

调用堆栈窗格显示调用了哪些函数来达到这一点。

Scope窗格显示所有可用的局部和全局变量的状态。

Breakpoints窗格显示所有断点的列表,并允许启用或禁用它们。

Debugger paused消息上方的图标可用于恢复执行、单步执行、单步执行、单步执行、单步执行、停用所有断点以及暂停异常。

使用 VS 代码进行 Node.js 调试

当您在本地系统上运行 Node.js 应用程序时,无需任何配置即可启动 VS Code Node.js 调试。打开起始文件(通常是index.js),激活运行和调试窗格,然后单击运行和调试 Node.js (F5)按钮。

调试屏幕类似于带有变量监视调用堆栈加载脚本断点列表的 Chrome DevTools。

可以通过单击行号旁边的装订线来设置断点。您也可以右键单击。

通过此右键单击,您可以设置以下内容:

  1. 标准断点。
  2. 满足条件时停止的条件断点,例如count > 3.
  3. 一个日志点,实际上console.log()没有代码!任何字符串都可以用花括号表示的表达式输入——例如,{count}显示count变量的值。

注意:不要忘记点击ReturnVS Code 来创建条件断点或日志点。

顶部的调试图标栏可用于恢复执行、单步执行、单步执行、单步执行、重新启动或停止应用程序和调试。菜单中的Debug项也提供相同的选项。

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

高级调试配置

当您调试远程服务或需要使用不同的启动选项时,需要进一步配置。VS Code 将启动配置存储在项目文件夹内launch.json生成的文件中。要生成或编辑文件,请单击“运行和调试”窗格.vscode右上角的齿轮图标。

可以将任意数量的配置设置添加到configurations阵列中。单击添加配置按钮以选择一个选项。VS Code 可以:

  1. 使用 Node.js 本身启动一个进程,或者
  2. 附加到 Node.js 检查器进程,可能在远程机器或 Docker 容器上运行

在上面的示例中,定义了单个 Nodemon 启动配置。Save launch.json,从Run and Debugnodemon窗格顶部的下拉列表中选择,然后单击绿色的开始图标。

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

其他 Node.js 调试工具

Node.js 调试指南为其他 IDE 和编辑器提供建议,包括 Visual Studio、JetBrains、WebStorm、Gitpod 和 Eclipse。Atom 还有一个节点调试扩展。

ndb通过附加到子进程和脚本黑盒等强大功能提供改进的调试体验,因此仅显示特定文件夹中的代码。

用于 Node.js的IBM 报告工具包通过在使用该选项node运行时分析数据输出来工作。–experimental-report

最后,LogRocket和Sentry.io等商业服务与您在客户端和服务器中的实时 Web 应用程序集成,以记录用户遇到的错误。

获取调试!

Node.js 有一系列出色的调试工具和代码分析器,可以提高应用程序的速度和可靠性。他们能不能诱惑你离开console.log()是另一回事!

加客服微信:qiushu0517,开通VIP下载权限!