做好异常处理,是实现项目规范化的基础

Jul 08, 2020
作为前端的小伙伴,天天写前端样式也会略微的枯燥,萌生想接触后端知识的想法。这不最近一段时间在后端大佬的熏陶下,也对后端的规范化的开发思想略知一二,先用koa来分享一下规范化建设之中的项目错误处理以及日志的持久化。

为什么选型Koa:

Koa 是一个新的 web 框架,它是由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。

Koa底层是基于Node.js,可以胜任IO密集型任务和高并发任务,并且它是一款轻量级的框架,对于前端开发人员来说可以很快上手。一般的项目Koa完全可以胜任,一方面前端人员自己接手写接口,可以减轻公司后端资源的压力。部署的时候占用的资源也是很少。

错误捕捉:

一个项目想要运行稳定:肯定会具备完善的错误处理机制以及日志记录功能。错误处理机制有利于我们在发现错误,排查和解决错误。日志的持久化则方便记载日志,另一方面也是防止宕机等事故销毁日志。

一般常用的错误处理方式是使用try catch,通过它可以保证线程的安全,但是许多地方可能也有不确定性,所以try catch只能用于确定可能出问题的地方。如果是不知道什么地方报异常,这时候try catch就无用武之地了,甚至服务挂掉也不知晓,还不能将出现的错误返回客户端。这里使用eventEmitter触发error来处理不确定的异常,这两者结合就可以灵活的将错误处理掉。

1.使用eventEmitter触发error处理不确定的错误

这个方案可以让系统处在稳定的环境中,防止系统宕机。这一点是考虑到koa框架的运行机制是基于洋葱模型的,也就是请求发送之后,按顺序从中间件0运行到中间件n-1,再通过next( )由内向外到达中间件0

这样一来,在middleware(0)这块加上一个emit的中间件,不管是什么地方有错误,它都会捕捉到,返回进行相应的处理操作,方便定位错误。

// 这里我写了一个错误捕捉的util
module.exports = function catchError(ctx, next) {
  return async (ctx, next)=>{
    try {
      await next();
    } catch (error) {
      ctx.status = error.statusCode || error.status || 500;
      ctx.body = {
        errMsg: error.message
      };
      ctx.app.emit('error', error); // 触发应用层级错误事件
    }
  }
}

最后在core这里添加一个error事件,上面的emit就会触发error,进行相应的错误处理操作,很优雅的将可能出现的错误处理掉,不用到处写异常处理的代码来将服务趋于稳定

app.on('error', (error)=>{
    console.error(error);
});

一般这些问题都是系统出现的错误,所以给它定义response status为500,并且将捕捉的错误详情返回给客户端

2. 捕捉局部错误:

使用try catch,如果哪个请求出现了错误,或者数据库操作出现错误了,就在catch语句块中处理错误,将这个错误信息返回给客户端。错误信息包含code(错误代码),和msg(错误信息),方便快速定位错误之处

日志的记录:

错误被捕捉到,那自然就要将它们持久化的存储下来。排查线上问题时,日志起到关键的作用。正如我们每天写日记一样,不仅能够记录项目每天都做了什么,便于日后回顾,也可以将做错的事情记录下来,进行自我反省。

这里我使用Winston记录日志,因为它是目前Node.js最流行的日志框架之一,支持多传输,分级记录不同级别的日志

贴一段记录异常的核心代码:

const { createLogger, format, transports } = require('winston');

function loggers(errMsg, errType) {
  const logger = createLogger({
    level: 'error',
    format: format.combine(
      format.timestamp({
        format: 'YYYY-MM-DD HH:mm:ss'
      }),
      format.errors({ stack: true }),
      format.splat(),
      format.json()
    ),
    defaultMeta: { errMsg }
  });

  logger.add(new transports.File({
    maxSize: '20m',
    maxFiles: '14d',
    level: 'error',
    filename: 'logs/%DATE%.log',
    format: format.combine(
      format.simple()
    )
  }));
  // 以json格式logging
  let type = errType === 'system' ? '系统错误' : '接口错误'
  logger.log({
    level: 'error',
    message: type,
  });
}

这里我通过设置日志文件最大为20M,将日志按天统一存在logs文件夹下。

存储的格式包含错误类型(通过捕捉时传的type不同来区分)、错误信息和时间,以下是存储的日志内容:

error: 系统错误 {"errMsg":"ctx is not defined","timestamp":"2020-07-08 15:04:37"}
error: 接口错误 {"errMsg":"参数类型有误, 'exampleParams' should be a boolean","timestamp":"2020-07-08 15:11:56"}

当然,以上的日志功能只是将捕捉到的异常持久化,功能单一。以后还可以根据业务的需求进行扩展,例如访问接口情况,登录情况都可以记载。

总结:

以上是我在koa实践中处理异常的方法和如何去记录日志的,这样做的好处是将不确定的错误统一处理,接口层面的错误使用try catch处理。两个方法结合使用,最后都将异常信息持久化,方便排查。达到了持续集成的目的,之后再开发就只需要关注业务逻辑就行。

评论正在加载...
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.
分享