Node.js 深度调试方法解析


Posted in Javascript onJuly 28, 2020

在 Node.js 项目开发过程中,随着项目的发展,调用关系越来越复杂,调试工具的重要性日益凸显。

Node(v6.3+)集成了方便好用 V8 Inspect 调试器,允许我们通过 Chrome DevTools 进行图形化的调试和性能分析。同时,我们也可以使用 VS Code,Webstorm 等支持的编辑器对 Node.js 程序进行调试。

Node Inspect

要想启动调试器,我们需要在启动 Node.js 应用程序时传入 --inspect 标志,也可以使用该标志提供自定义的端口,例如 --inspect=9222 将会在 9222 端口上接受开发者工具的连接。

一段简单的代码

function log() {
 let a = 1;
 console.log(a);
 a = 2;
 console.log(a);
 }
 ​
 log();

使用 node --inspect 启动

Node.js 深度调试方法解析

这时我们会发现,程序直接执行完成了,没有中断,导致我们无法使用 Chrome DevTools 进行调试。对于这种直接执行的代码,我们可以使用 --inspect-brk 参数,在应用程序代码的第一行终端,然后再进行调试。

Node.js 深度调试方法解析

Chrome DevTools

当开启 Node 调试后,我们可以打开 Chrome,访问 chrome://inspect ,在 Devices 中查找到我们的 Node.js 程序,点击 inspect 打开调试面板进行操作

Node.js 深度调试方法解析

在调试工具窗口,我们可以设置断点,运行程序进行调试

Node.js 深度调试方法解析

运行中程序调试

在某些情况下,我们可以需要对正在运行的 Node.js 程序进行调试,比如 Express Web 服务。我们不可能停止服务,再以 --inspect 运行调试。

对于这种情况,我们可以先获取服务的进程 Id

Node.js 深度调试方法解析

向脚本进程发送 SIGUSR1 信号,就可以建立调试连接

kill -SIGUSR1 34943
复制代码

Node.js 深度调试方法解析

在 Windows 平台下,可以使用下面的命令

node -e 'process._debugProcess(30464)'

需要注意的是:这种调试任然会中断服务进程的执行。

VS Code 调试

快速调试

对于简单的应用程序,可以打开文件,按 F5 并选择调试类型为 Node,即可进行调试

Node.js 深度调试方法解析

使用配置调试

对于大多数的调试场景,更推荐使用配置文件,因为它可以配置并保存调试设置的信息,方便我们下次快速使用。在 VC Code 中,调试配置通常存储在 .vscode 文件夹下的 launch.json 文件中 。可以点击左侧栏目中的调试图标,快速创建 launch.json 文件

Node.js 深度调试方法解析

VS Code 会自动下面类似的 launch.json 调试配置文件,其中 program 代表我们需要调试的文件路径,workspaceFolder 为当前工作区的路径,通常是项目的根目录

{
 "version": "0.2.0",
 "configurations": [
  {
  "type": "node",
  "request": "launch",
  "name": "启动程序",
  "skipFiles": ["<node_internals>/**"],
  "program": "${workspaceFolder}/index.js"
  }
 ]
 }

设置断点,即可启动调试,并在左侧的树视图中看到变量对应的值以及堆栈信息

Node.js 深度调试方法解析

launch.json

launch.json 中有许多不同的属性,支持不同的调试器和调试场景,下面的属性在每个启动配置中是必须的

  • name - 当前调试配置项的名称,可读性要好,区分每个调试配置项
  • type - 用于此启动配置的调试器的类型。每个已安装的调试扩展都引入一种类型:例如node,php,go 等。
  • request - 当前调试项的类型,目前支持 launch 和 attach 两种类型。launch 适合调试未启动的程序,attach 则适合调试已经运行的程序。

一些其他比较有用的选项:

  • program - 启动调试器时要运行的可执行程序或文件
  • args - 传递给程序进行调试的参数
  • env - 调试时的环境变量
  • envFile - 包含环境变量键值对的文件
  • stopOnEntry - 程序启动时立即中断
  • port - 连接到正在运行的调试器的端口
  • runtimeExecutable - 启用调试的可执行 Runtime,默认是 Node

日志点 - Logpoints

VS Code 提供了好用的调试小工具 - 日志点,日志点是断点的一种变体,它不 "中断 "进入调试器,而是将一条消息记录到控制台,日志点对于在调试不能暂停或停止的生产服务器时注入日志特别有用。

Node.js 深度调试方法解析

NPM 脚本调试

除了使用 node 启动 Node.js 项目之外,VS Code 还支持自定义启动程序 runtime,借助这个能力,可以直接使用 NPM 启动调试。如下面,使用 npm run debug 启动调试

{
 "version": "0.2.0",
 "configurations": [
  {
  "type": "node",
  "request": "launch",
  "name": "启动程序",
  "skipFiles": ["<node_internals>/**"],
  "program": "${workspaceFolder}/index.js"
  }
 ]
 }

launch.json

{
 "type": "node",
 "request": "launch",
 "name": "NPM 启动",
 "runtimeExecutable": "npm",
 "runtimeArgs": ["run", "debug"],
 "port": 9229
 }

TypeScript 调试

VS Code 内置的 Node.js 的调试器支持 JavaScript Source Map,可以结合 Source Map 调试转译前的代码,如 TypeScript,压缩混淆的 JavaScript 代码等都可以利用 Source Map 的支持调试源码。

我准备了一个简单的 TS Server Demo,可以直接 Clone 源码本地测试。下面是项目中的 src/index.ts 文件,创建了一个 HTTP Server

import * as http from "http";
 ​
 let reqCount = 1;
 ​
 http
 .createServer((req, res) => {
  const message = `Request Count: ${reqCount}`;
 ​
  res.writeHead(200, { "Content-Type": "text/html" });
 ​
  res.end(`<html><div>${message}</div></html>`);
 ​
  console.log("handled request: " + reqCount++);
 })
 .listen(3000);
 ​
 console.log("server running on port 3000");

创建 tsconfig.json 配置,配置编译生成 Source Map

{
 "compilerOptions": {
  "outDir": "./dist",
  "sourceMap": true
 },
 "include": ["src/**/*"]
 }

使用 tsc 编译一下,生成 JS 代码:dist/index.js,创建调试配置,入口文件为 dist/index.js

{
 "type": "node",
 "request": "launch",
 "name": "Launch Program",
 "program": "${workspaceFolder}/dist/index.js",
 "skipFiles": ["<node_internals>/**"]
 }

然后打断点,启动调试,浏览器访问 http://localhost:3000,即可看到调试进入了 TS 文件

Node.js 深度调试方法解析

远程调试

当我们需要在真实的服务器等远程运行环境调试 Node.js 时,我们可以利用上面提到的方式,在服务器上开启 Node.js 调试功能,并在本地连接上远程的调试端口进行调试。

VS Code 默认支持远程调试,我们需要 launch.json 配置文件中指定远程服务的 IP 地址以及端口,如下所示:

{
 "type": "node",
 "request": "attach",
 "name": "远程调试",
 "address": "IP 地址",
 "port": "9229"
 }

VS Code 会自动加载远程的文件,展示为只读代码供调试使用。

如果想要在调试的过程中编辑源代码,或者更好的调试体验,可以在远程文件夹和本地项目之间设置一个映射。VS Code 提供了 localRoot 和 remoteRoot 属性来映射本地 VS Code 项目和(远程)Node.js 文件夹:

{
 "type": "node",
 "request": "attach",
 "name": "远程调试",
 "address": "IP 地址",
 "port": "9229",
 "localRoot": "${workspaceFolder}/src",
 "remoteRoot": "/var/user/"
 }

在建立映射关系后,即可在本地项目进行断点调试,远程的断点信息会同步到本地项目,使用起来十分方便。

子进程调试

与普通进程调试原理一致,子进程调试时也需要传入 --inspect 参数,这一点需要特别注意,否则无法启动子进程调试。

如下通过子进程启动 Server 的例子:

// fork.js 文件
 const { spawn } = require("child_process");
 ​
 const sp = spawn("node", ["./fork_server.js"]);
 ​
 console.log("父进程 PID", sp.pid);
 ​
 sp.stdout.on("data", (data) => {
 console.log(`stdout: ${data}`);
 });
 ​
 sp.stderr.on("data", (data) => {
 console.error(`stderr: ${data}`);
 });

如果直接使用 node --inspect 启动主进程的话,会发现只显示了主进程的调试端口,这就是因为我们在程序中启动子进程时没有传递 --inspect 选项导致的。

Node.js 深度调试方法解析

这里我们在启动进程时添加上 --inspect 参数,同时注意要指定一个默认 9229 端口之外的端口号,避免调试端口冲突

- const sp = spawn("node", ["./fork_server.js"]);
 + const sp = spawn("node", ["--inspect=9230", "./fork_server.js"]);

再次启动,就能看到两个调试信息输出了

Node.js 深度调试方法解析

当然,怎么能少得了强大的 VS Code 呢。VS Code 的 Node 调试器提供了一种机制,可以追踪所有子进程,并在调试模式下,自动链接进程。可以通过 autoAttachChildProcesses 属性开启此机制:

{
 "type": "node",
 "request": "launch",
 "name": "启动程序",
 "program": "${workspaceFolder}/fork.js",
 "autoAttachChildProcesses": true
 }

启动后,即可对父进程,或子进程进行断点调试,效果如下

Node.js 深度调试方法解析

结语

到此这篇关于Node.js 深度调试方法解析的文章就介绍到这了,更多相关Node.js 深度调方法内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
ext 同步和异步示例代码
Sep 18 Javascript
javascript在子页面中函数无法调试问题解决方法
Jan 17 Javascript
JS中FRAME的操作问题实例分析
Oct 21 Javascript
jquery实现初次打开有动画效果的网页TAB切换代码
Sep 06 Javascript
vue 循环加载数据并获取第一条记录的方法
Sep 26 Javascript
从零开始实现Vue简单的Toast插件
Dec 03 Javascript
详解基于React.js和Node.js的SSR实现方案
Mar 21 Javascript
基于vue实现一个禅道主页拖拽效果
May 27 Javascript
vue2 中二级路由高亮问题及配置方法
Jun 10 Javascript
angularjs自定义过滤器demo示例
Aug 24 Javascript
vue实现文件上传读取及下载功能
Nov 17 Javascript
js实现跳一跳小游戏
Jul 31 Javascript
vue-列表下详情的展开与折叠案例
Jul 28 #Javascript
js 数组当前行添加数据方法详解
Jul 28 #Javascript
Vue 解决通过this.$refs来获取DOM或者组件报错问题
Jul 28 #Javascript
JS代码简洁方式之函数方法详解
Jul 28 #Javascript
vue 组件之间事件触发($emit)与event Bus($on)的用法说明
Jul 28 #Javascript
JavaScript前端开发时数值运算的小技巧
Jul 28 #Javascript
js实现全选和全不选
Jul 28 #Javascript
You might like
PHP简单系统查询模块代码打包下载
2008/06/07 PHP
ThinkPHP验证码使用简明教程
2014/03/05 PHP
yii中widget的用法
2014/12/03 PHP
PHP获取当前日期和时间及格式化方法参数
2015/05/11 PHP
Yii2.0多文件上传实例说明
2017/07/24 PHP
JS小功能(setInterval实现图片效果显示时间)实例代码
2013/11/28 Javascript
javascript操纵OGNL标签示例代码
2014/06/16 Javascript
javascript中判断json的方法总结
2015/08/27 Javascript
JavaScript使用DeviceOne开发实战(二) 生成调试安装包
2015/12/01 Javascript
jQuery中$.ajax()方法参数解析
2016/10/22 Javascript
微信公众平台开发教程(四) 实例入门:机器人回复(附源码)
2016/12/02 Javascript
BootStrap Table 设置height表头与内容无法对齐的问题
2016/12/28 Javascript
babel基本使用详解
2017/02/17 Javascript
jquery层次选择器的介绍
2019/01/18 jQuery
详解Vue中组件的缓存
2019/04/20 Javascript
jquery多级树形下拉菜单的实例代码
2019/07/09 jQuery
浅析JavaScript中的事件委托机制跟深浅拷贝
2021/01/20 Javascript
[05:20]卡尔工作室_DOTA2新手教学_DOTA2超强新手功能
2013/04/22 DOTA
[02:54]DOTA2英雄基础教程 暗影牧师戴泽
2013/12/05 DOTA
[36:29]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 LGD vs TNC
2018/04/02 DOTA
用Python实现QQ游戏大家来找茬辅助工具
2014/09/14 Python
实例说明Python中比较运算符的使用
2015/05/13 Python
RC4文件加密的python实现方法
2015/06/30 Python
python实现朴素贝叶斯分类器
2018/03/28 Python
使用sklearn进行对数据标准化、归一化以及将数据还原的方法
2018/07/11 Python
Python 可变类型和不可变类型及引用过程解析
2019/09/27 Python
基于python实现删除指定文件类型
2020/07/21 Python
如何使用Python调整图像大小
2020/09/26 Python
python中复数的共轭复数知识点总结
2020/12/06 Python
init进程的作用
2012/04/12 面试题
主题酒店策划书
2014/01/28 职场文书
公安纪律作风整顿剖析材料
2014/10/10 职场文书
天堂的孩子观后感
2015/06/11 职场文书
2019最新版火锅店的创业计划书 !
2019/07/12 职场文书
Python Pandas模块实现数据的统计分析的方法
2021/06/24 Python
MySQL数据库实验实现简单数据库应用系统设计
2022/06/21 MySQL