node异常处理

457 阅读3分钟

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"
    }
  }]
};