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 相关文章推荐
JavaScript的面向对象方法以及差别
Mar 31 Javascript
setInterval与clearInterval的使用示例代码
Jan 28 Javascript
浅谈 jQuery 事件源码定位问题
Jun 18 Javascript
完美兼容各大浏览器获取HTTP_REFERER方法总结
Jun 24 Javascript
影响jQuery使用的14个方面
Sep 01 Javascript
Flash图片上传组件 swfupload使用指南
Mar 14 Javascript
js验证真实姓名与身份证号是否匹配
Oct 13 Javascript
微信小程序用户自定义模版用法实例分析
Nov 28 Javascript
JS删除数组里的某个元素方法
Feb 03 Javascript
JavaScript变量提升和严格模式实例分析
Jan 27 Javascript
JS图片懒加载技术实现过程解析
Jul 27 Javascript
jquery实现图片放大镜效果
Dec 23 jQuery
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
html中select语句读取mysql表中内容
2006/10/09 PHP
Android ProgressBar进度条和ProgressDialog进度框的展示DEMO
2013/06/19 PHP
async和DOM Script文件加载比较
2014/07/20 PHP
帝国CMS留言板回复后发送EMAIL通知客户
2015/07/06 PHP
为JavaScript类型增加方法的实现代码(增加功能)
2011/12/29 Javascript
给jQuery方法添加回调函数一款插件的应用
2013/01/21 Javascript
javascript拖拽效果延伸学习
2016/04/04 Javascript
JavaScript实现简单的日历效果
2016/09/25 Javascript
Javascript ES6中数据类型Symbol的使用详解
2017/05/02 Javascript
angularjs实现上拉加载和下拉刷新数据功能
2017/06/12 Javascript
理解Angular的providers给Http添加默认headers
2017/07/04 Javascript
百度地图去掉marker覆盖物或者去掉maker的label文字方法
2018/01/26 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
2018/12/13 Javascript
微信小程序实现获取准确的腾讯定位地址功能示例
2019/03/27 Javascript
jQuery实现的移动端图片缩放功能组件示例
2020/05/01 jQuery
python遍历文件夹并删除特定格式文件的示例
2014/03/05 Python
Python使用迭代器打印螺旋矩阵的思路及代码示例
2016/07/02 Python
python字符串中的单双引
2017/02/16 Python
Numpy数据类型转换astype,dtype的方法
2018/06/09 Python
python的中异常处理机制
2018/08/30 Python
linux安装Python3.4.2的操作方法
2018/09/28 Python
python实现字符串加密 生成唯一固定长度字符串
2019/03/22 Python
Python CSS选择器爬取京东网商品信息过程解析
2020/06/01 Python
简单了解如何封装自己的Python包
2020/07/08 Python
美国在线奢侈品寄售商店:Luxury Garage Sale
2018/08/19 全球购物
线程的基本概念、线程的基本状态以及状态之间的关系
2012/10/26 面试题
如何理解transaction事务的概念
2015/05/27 面试题
大学新生军训个人的自我评价
2013/10/03 职场文书
数控技术专科生自我评价
2014/01/08 职场文书
读书活动总结范文
2014/04/26 职场文书
志愿者爱心公益活动策划方案
2014/09/15 职场文书
房产公证委托书范本
2014/09/20 职场文书
群众路线自我剖析范文
2014/11/04 职场文书
对学校的意见和建议
2015/06/04 职场文书
美容院员工规章制度
2015/08/05 职场文书
vue中div禁止点击事件的实现
2022/04/02 Vue.js