node异常处理
在应用出现异常且未及时处理会导致程序崩溃,同时在异常发生时,能够第一时间捕获并获取足够的信息来定位问题。 通过异常上报,可以监控应用的运行情况,预防可能会出现的问题。
异常的分类
一、同步代码的异常捕获处理
- 使用try catch结构捕获异常
- 使用process的uncaughtException事件进行全局的异常捕获
二、异步代码的错误处理
- promise可以使用promise的api中catch捕获
- 可以使用async将promise转化为同步代码后使用try catch捕获
- 使用process的unhandledRejection事件进行全局的异常捕获
异步代码不能直接用try catch来捕获异常。因为事件循环机制,异步任务是通过事件队列来实现的,每次从时间队列中取出异步任务来执行,此时这个异步任务运行在调用栈的顶层。此时如果出现异常就无法通过调用栈回溯到这个异步任务的创建者。
API层异常结构化
当API层发生异常时,服务端需要将发送到客户端的异常进行结构化,目的是方便异常的上报和前端展示。一般在最顶层的中间件中捕获并进行相应的处理,以下以koa为例
app.use(async (ctx, next) => {
try {
await next();
} catch (error) {
ctx.body = error.toString();
}
})
name
错误标识码,用来对错误进行分类。
message
异常信息摘要
info
针对 code 的更为详细的信息,比如数据校验失败的具体字段。
context
异常发生时的上下文,堆栈信息 可以使用V8提供的API来设置,v8.dev/docs/stack-…
status
状态码
errorId
异常的唯一标识(uuid),便于用户反馈时来定位具体异常。
const uuidv4 = require('uuid').v4;
const util = require('util');
// 覆写Error类的toString方法
Error.prototype.toString = function() {
return `${this.stack}`;
};
// class版本,继承于Error,已经带有堆栈信息
class cumstomError extends Error {
constructor(name, message, status) {
super(name, message);
this.status = status;
this.errorId = uuidv4();
}
toString() {
return `${this.stack} ${this.status ? '\n status:' + this.status : ''} ${this.errorId ? '\n errorId:' + this.errorId : ''}`;
}
}
// 函数版本,captureStackTrace由V8中的Error提供,用于加上堆栈信息。
function MyError(name, message, status) {
this.name = name;
this.message = message;
this.status = status;
this.errorId = uuidv4();
Error.captureStackTrace(this);
return this;
}
util.inherits(MyError, Error);
MyError.prototype.toString = function () {
return `${this.stack} ${this.status ? '\n status:' + this.status : ''} ${this.errorId ? '\n errorId:' + this.errorId : ''}`;
};
利用pm2启动进程守护
一些错误是从Node底层抛出的,无法通过try catch和uncaughtException捕获,因此需要一个机制来保证进程在奔溃之后能够自动重启。
// pm2配置文件
module.exports = {
apps : [{
script: 'app.js', // 执行脚本文件
watch: '.', // 所监听的文件
instances : "2", // 实例数
exec_mode : "cluster", // 执行模式
ignore_watch: ["node_modules","logs"], // 监听时忽略的文件
max_memory_restart: "150M", // 超过最大内存重启
cwd: "./", // 运行目录
error_file: "./logs/app-err.log", // 错误日志文件
out_file: "./logs/app-out.log", // 正常日志文件
merge_logs: true, // 设置追加日志而不是新建日志
log_date_format: "YYYY-MM-DD HH:mm:ss", // 指定日志文件的时间格式
min_uptime: "60s", // 应用运行少于时间被认为是异常启动
max_restarts: 30, // 最大异常重启次数
autorestart: true, // 默认为 true, 发生异常的情况下自动重启
restart_delay: "60", // 异常重启情况下,延时重启时间
env: { // 默认模式
"NODE_ENV": "development"
},
env_production: { // 附带参数--env production时
"NODE_ENV": "production"
}
}]
};