第一章已经介绍过全局的异常处理了,但之前的做法过于简单,一股脑的捕获并返回。这一节我们将对异常进行细致的分类,并且日志也做标准化的分级。
一个基础的 evp-express 项目
先了解一下 NodeJS 里面的异常:
JavaScript 异常是由于无效操作或作为 throw 语句的目标而抛出的值。 虽然不要求这些值是 Error 的实例或从 Error 继承的类,但 Node.js 或 JavaScript 运行时抛出的所有异常都将是 Error 的实例。
在 NodeJS 中,所有抛出(throw)的异常都可以被归为通用的 Error,此外还内置了更细分的 AssertionError, SystemError, SyntaxError 等。
Error:
new Error(message[, options])
error.cause
:引起 error 的根本原因,可以在 new 时的options中加入error.message
:错误的描述error.stack
:错误栈,产生 error 的位置evp-express 的错误中间件已经把异常分类做好了,分成两部分 excather 和 exlogger,excatcher 捕获并处理异常,exlogger 则记录日志。我们先来看怎么对异常自定义分类。
自定义分类的方法,其实很简单,无非是在抛出的异常上增加用以标识分类的属性即可。我没有选择这种方式,手动抛出时要略微多写几行代码,error.message 是一个描述字符串,我选择抛出自定义异常时,传递的message是一个json字符串,json里面再放置具体描述,分类信息以及其它等等的信息载体。
throw new Error(JSON.stringfy(
code: 400,
msg: "自定义异常1",
back: true
));
接下来看看异常处理器如何处理自定义异常和其它异常。
excatcher做了三件事,捕获异常,解析异常和根据策略调取异常处理器。
excatcher: (err, req, res, next) => {
if (err) {
try {
const payload = JSON.parse(err.message);
const {code,msg,symbol,data,back,status} = payload;
if (back != false) {
const handler = exhandleStrategy.get(code);
if ( handler ) { handler(res,msg,symbol,data,status); }
else { res.json(Resp.bad("System Exception")); }
} else {
res.json(Resp.bad("System Exception"));
}
} catch (_) { // cannot be parsed to JSON object => common error
res.json(Resp.bad("System Exception"));
}
next(err);
} else {
next();
}
},
exhandleStrategy:
const exhandleStrategy = new Map();
exhandleStrategy.set(400, (res, ...args)=>{
const [msg,symbol,data,status] = args;
if (status) {
res.status(status).json(Resp.fail(msg, symbol??-1, data??null));
} else {
res.json(Resp.fail(msg, symbol??-1, data??null));
}
});
exhandleStrategy.set(500, (res, ...args)=>{
const [msg,symbol,data,status] = args;
if (status) {
res.status(status).json(Resp.bad(msg));
} else {
res.json(Resp.bad(msg));
}
})
接下来我们看看 exlogger
exlogger 进行了一个判断,如果当前日志级别 <= 10000,即 DEBUG,就记录完整的异常信息,否则只记录异常的描述信息。
exlogger: (err,req,res,next)=>{
if (logger.level.level <= 10000) {
logger.error(err);
return;
}
logger.error(err.message);
}
logger是什么呢?这是 log4js 的日志器,让我们来看看utils/logger.js
我们使用 log4js.js 作为日志框架,log4js.configure 配置了 log4js 的基本配置,日志格式,日志方式(输出、文件等)、日志级别等,日志级别从 evp-express 的项目配置信息中读取而得。
const config = require('../config').get();
const log4js = require('log4js');
let logger = log4js.getLogger("");
log4js.configure({
appenders: {
out: {
type: "stdout",
layout: {
"type": "pattern",
"pattern": "[%d{yyyy-MM-dd hh:mm:ss}] %p %m"
}
}
},
categories: {
default: {
appenders: ["out"],
level: config.log4js.level
}
}
})
module.exports = logger;
日志级别总共有 “ALL”,“TRACE”, “DEBUG”, “INFO”, “WARN”, “ERROR”, “FATAL”(致命), “MARK”, “OFF”(关闭) 这几级(大小写均支持),重要性逐级递增,在当前设置的 level 之下的日志将不会被记录,如日志级别设置为 “INFO”,则 logger.all, logger.trace, logger.debug 将不会进行日志记录。
这些文字的级别对应以下数字,依次为 5e-324
, 5000
, 10000
, 20000
, 30000
, 40000
, 50000
, 9007199254740992
, 1.7976931348623157e+308
日志器当前的级别可由 logger.level
或者 logger.level.levelStr
获取,当前级别对应的数字可由 logger.level.level
获取,不过还是更建议自定义一个数组或者Map,把所有的级别文字按序放入,然后需要的时候根据索引来判断级别的高低。