Node.js 调试与错误处理

代码写完不等于能跑——一定会有 bug。这一章讲 Node 的调试工具箱:从最基础的 console.log,到 Chrome DevTools 可视化断点,再到 VS Code 的图形化调试器,以及生产环境的错误处理和日志方案。学会高效调试,你的开发效率会成倍提升。

1. console:最基础也最常用

别小看 console.log——它解决 80% 的日常问题。但 console 家族远不止 log,用好它的兄弟姐妹能让调试事半功倍:

// console 不只是 console.log,还有一堆兄弟

console.log("普通日志");
console.info("信息(和 log 几乎一样)");
console.warn("警告(黄色)");
console.error("错误(红色)");

// 打印对象(树状展开,看嵌套最直观)
const user = { name: "小明", age: 20, addr: { city: "北京" } };
console.log(user);
console.dir(user, { depth: null, colors: true });

// 带标签的分组输出
console.group("用户详情");
console.log("姓名:", user.name);
console.log("年龄:", user.age);
console.groupEnd();

// 计时:看一段代码执行多久
console.time("loop");
for (let i = 0; i < 1000000; i++) {}
console.timeEnd("loop");
// 输出: loop: 3.245ms

// 打印堆栈(看调用链)
console.trace("我在哪被调用的?");

几个实战技巧:

2. Node inspector:Chrome DevTools 调试

当 console 不够用时(复杂逻辑、异步时序问题),就要上真正的断点调试。Node 内置 inspector,可以用 Chrome DevTools 可视化调试:

# Node 内置 inspector:用 Chrome DevTools 调试 Node

# 启动时加 --inspect 参数
node --inspect server.js
# 监听 127.0.0.1:9229,等待调试器连接

# 想在第一行就断下(适合调试启动过程)
node --inspect-brk server.js

# 然后:
# 1. 打开 Chrome 浏览器
# 2. 地址栏输入 chrome://inspect
# 3. 点 "inspect" 链接,会弹出 DevTools
# 4. 在 Sources 面板可以打断点、看变量、单步执行

# 远程调试(调试服务器上的 Node)
node --inspect=0.0.0.0:9229 server.js
# 警告:0.0.0.0 暴露到公网有安全风险,只在受信网络用

DevTools 里你能做:

3. VS Code 调试(推荐新手)

VS Code 内置了 Node 调试器,不用切换浏览器,直接在编辑器里按 F5 调试。这是日常开发最方便的方式:

// VS Code 内置 Node 调试器,图形化操作最适合新手
// 1. 打开要调试的文件
// 2. 在行号左边点一下,设置红色断点
// 3. 按 F5(或 Run 面板 → 启动调试)

// 也可以在 .vscode/launch.json 里配置
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "调试当前文件",
      "program": "${workspaceFolder}/index.js",
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "type": "node",
      "request": "attach",
      "name": "附加到进程",
      "port": 9229
    }
  ]
}

// launch:VS Code 自己启动 Node 并附加调试器
// attach:Node 已经在跑(可能用 --inspect 启动),VS Code 连上去
// 调试时:看变量、条件断点、log point、调用栈、watch 都能用

VS Code 调试的几个高级功能:

4. debugger 语句:代码里的断点

在代码里写 debugger; 语句,等于手动插入一个断点。配合 inspector 模式,执行到这行会暂停:

// 代码里写 debugger 语句,等于手动打断点
// --inspect 模式下,执行到这行会暂停

function calculate(a, b) {
  const result = a * b + 42;
  debugger;   // 调试器在这里会暂停
  return result;
}

calculate(3, 4);
// 配合 node --inspect 使用,排查具体函数逻辑很方便

好处是不用去 DevTools 里找文件设断点,直接在代码里标记。但发布前要删掉,不然用户调试模式会卡住。

5. 错误堆栈与自定义错误

JS 的 Error 对象带堆栈信息(stack trace),告诉你"错误从哪抛出、经过哪些函数调用"。读懂堆栈是高级开发的基本功:

// 错误对象带堆栈信息(stack trace)
// 它告诉你"错误从哪抛出、经过哪些函数调用"

function level3() {
  throw new Error("出错了!");
}

function level2() {
  level3();
}

function level1() {
  level2();
}

try {
  level1();
} catch (err) {
  console.error("错误消息:", err.message);
  console.error("堆栈:");
  console.error(err.stack);
  // Error: 出错了!
  //     at level3 (app.js:2:11)
  //     at level2 (app.js:6:3)
  //     at level1 (app.js:10:3)
  //     at Object.<anonymous> (app.js:13:3)
}

// 自定义错误:带更多上下文
class HttpError extends Error {
  constructor(status, message) {
    super(message);
    this.name = "HttpError";
    this.status = status;
  }
}
throw new HttpError(404, "用户不存在");

堆栈从上到下是"由内到外"——最上面是抛出错误的地方,往下是调用链。读到 app.js:2:11 这种"文件名:行号:列号",直接定位到出问题的代码。自定义 Error 类能携带更多业务信息(如 HTTP 状态码),让错误处理更精细。

6. 新手常见错误速查

// Node 新手最常遇到的几种错误

// 1. Cannot find module 'xxx'
//   原因:模块名写错,或忘了 npm install
//   解决:检查拼写,跑 npm install xxx

// 2. EADDRINUSE: address already in use :::3000
//   原因:3000 端口被别的进程占了
//   解决:lsof -i:3000 查占用的进程,kill 掉;或换个端口

// 3. ECONNREFUSED
//   原因:连的服务器(数据库、API)没起,或端口/地址错
//   解决:确认目标服务在跑,端口对得上

// 4. undefined is not a function / Cannot read property 'x' of undefined
//   原因:变量是 undefined,你却当对象用
//   解决:用可选链 obj?.x,或先判 if (obj)

// 5. Maximum call stack size exceeded
//   原因:无限递归,函数一直调用自己
//   解决:检查递归的终止条件

// 6. Promises must have a catch
//   原因:Promise 没 catch,失败时漂浮
//   解决:链尾加 .catch,或用 async/await + try/catch

遇到不认识的错误,直接复制错误信息去 Google/Stack Overflow,99% 都有人遇到过。学会读错误信息的关键词(EADDRINUSECannot find moduleECONNREFUSED),排查速度会快很多。

7. 生产环境日志

console.log 开发时方便,但生产环境必须用专业日志库——能分级别、写文件、结构化(JSON)、易于收集分析:

// 生产环境不能靠 console.log,要用专业日志库

// 主流选择:
//   winston:最经典,功能全
//   pino:主打性能,JSON 格式,极快
//   log4js:类似 Java log4j,配置灵活

// pino 示例(推荐,性能最好)
const pino = require("pino");
const logger = pino({
  level: "info",        // 最低级别
  transport: {
    target: "pino-pretty",
    options: { colorize: true }
  }
});

logger.info("服务启动");
logger.warn({ mem: process.memoryUsage() }, "内存占用");
logger.error({ err }, "请求失败");

// 日志级别(从低到高):
//   debug → info → warn → error → fatal
// 开发环境用 debug,生产用 info 或 warn
// 配合 ELK / Loki / Datadog 等日志系统集中收集分析

日志是生产排查问题的唯一线索。出 bug 时,好的日志能让你快速定位;差的日志(或没日志)只能盲猜。原则:

8. 进程管理与崩溃恢复

线上 Node 进程崩了怎么办?直接 node app.js 跑的进程崩了就没了。生产环境用进程管理器自动重启:

# PM2:最流行的 Node 进程管理器
# 全局安装
npm install -g pm2

# 启动应用(崩溃自动重启)
pm2 start app.js --name "my-api"

# 常用命令
pm2 list              # 查看所有应用
pm2 logs my-api       # 看日志
pm2 restart my-api    # 重启
pm2 stop my-api       # 停止
pm2 delete my-api     # 删除

# 开机自启(生成系统服务)
pm2 startup
pm2 save

# 集群模式(利用多核)
pm2 start app.js -i max    # 启动 CPU 核心数个进程

# 或者用 Docker + 容器编排(Kubernetes)
# 容器崩了编排系统自动拉起新容器

PM2 的核心价值:崩溃自动重启集群模式利用多核(Node 单进程只用一个核,PM2 起多份分担)、日志管理零停机重载(pm2 reload)。生产部署 Node 几乎必用。

9. 性能分析(进阶)

服务慢,怎么找出瓶颈?Node 内置性能分析工具:

性能优化的黄金法则:先测量再优化。不要凭感觉优化,用工具找到真正的瓶颈(80% 的时间往往花在 20% 的代码上)。

小结

调试是开发的日常。记住几条:console 解决 80% 问题;VS Code 按 F5 是最方便的图形化调试;读懂错误堆栈是基本功;生产环境用日志库 + 进程管理器。学会高效调试,你写代码的速度会快得多——因为不用再"瞎猜"。

← 上一篇 Node.js Express 框架

返回 Node.js 教程目录

✈️💬