视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
关于自定义Egg.js的请求级别日志详解
2020-11-27 22:02:33 责编:小采
文档


统一格式之后,业务现有业务的日志工具打印出来的格式是无法满足该规范的,所以我们需要对此进行改造。

我们前端目前Node中间层使用的框架是Egg.js,所以下文讲述下如何在Egg.js上自定义请求日志格式。

开始动手

Egg.js中自带了三种logger,分别是

  • Context Logger
  • App Logger
  • Agent Logger
  • Context Logger主要是用来记录请求相关的日志。每行日志都会在开头自动的记录当前请求的一些信息,比如时间、ip、请求url等等。

    App Logger用于记录应用级别的日志,比如程序启动日志。

    Agent Logger用于记录多进程模式运行下的日志。

    我们想自定义请求级别的日志,那重点就要从Context Logger去研究怎么做。最理想的方案就是,Context Logger本身支持配置化的自定义格式,通过在egg.js的config配置文件中,通过传入formatter的参数就能自定义。

    //config.default.js
    exports.customLogger = {
     log: {
     file: 'appname.log',
     formatter: (message)=>{
     return `${message.time}${message.processid}` 
     }
     }
    }

    但不久我们发现这条路走不通,设置了这个formatter并不起作用。从Context Logger的源码中,我们发现的端倪context_logger.js

    [ 'error', 'warn', 'info', 'debug' ].forEach(level => {
     const LEVEL = level.toUpperCase();
     ContextLogger.prototype[level] = function() {
     const meta = {
     formatter: contextFormatter,
     paddingMessage: this.paddingMessage,
     };
     this._logger.log(LEVEL, arguments, meta);
     };
    });
    
    module.exports = ContextLogger;
    
    function contextFormatter(meta) {
     return meta.date + ' ' + meta.level + ' ' + meta.pid + ' ' + meta.paddingMessage + ' ' + meta.message;
    }

    在源码中我们可以看到,formatter参数已经被内部的一个自定义格式化函数覆盖了,配置中写的是不会启作用的。
    此路不通,只能尝试自己实现logger去解决。自己实现我们需要考虑一些点,比如:

  • 日志要写到文件中,错误日志单独写一个文件
  • 需要能按天或按小时切割日志
  • IO性能
  • 如果这些都自己实现的话,那就太麻烦了。好在了解到Egg的这几个logger都是基于egg-logger和egg-logrotator去实现的,所以我们可以站在巨人的肩膀上搞事情。

    Context Logger是基于egg-logger的FileTransport类去进行文件落地的,同时FileTransport也默认配置了egg-logrotator的日志拆分。所以,我们只需要继承FileTransport类,实现接口就可以了,代码如下:

    //CoustomTransport.js
    const FileTransport = require('egg-logger').FileTransport;
    const moment = require('moment');
    
    class CoustomTransport extends FileTransport {
     constructor(options, ctx) {
     super(options);
     this.ctx = ctx;
     }
    
     log(level, args, meta) {
     const prefixStr = this.buildFormat(level);
     for (let i in args) {
     if (args.hasOwnProperty(i)) {
     if (parseInt(i, 10) === 0) {
     args[i] = `${prefixStr}${args[i]}`;
     }
     if (parseInt(i, 10) === args.length - 1) {
     args[i] += '\n';
     }
     }
     }
    
     super.log(level, args, meta);
     }
    
     buildFormat(level) {
     const timeStr = `[${moment().format('YYYY-MM-DD HH:mm:ss.SSS')}]`;
     const threadNameStr = `[${process.pid}]`;
     const urlStr = `[${this.ctx.request.url}]`
     return `${timeStr}${threadNameStr}${urlStr}`;
     }
    
     setUserId(userId) {
     this.userId = userId;
     }
    }
    
    module.exports = CoustomTransport;

    我们通过 logger.info('Hello World')去打印日志,格式则显示为我们自定义的格式。

    到这,自定义日志格式解决了,那我们如何获取每次请求的信息呢?这里就要借助Egg.js框架对Context的扩展功能, Context是请求级别的对象,我们在Context的原型上扩展方法可以拿到该对象带有的每次请求的信息。

    //CustomLogger.js
    const Logger = require('egg-logger').Logger;
    const CoustomTransport = require('./CoustomTransport.js');
    
    module.exports = function(ctx){
     const logger = new Logger();
     logger.set('file', new CoustomTransport({
     level: 'INFO',
     file: 'app.log'
     }, ctx));
     return logger;
    };
    
    // app/extend/context.js
    /*
    * Context对象扩展
    * */
    const Logger = require('egg-logger').Logger;
    const CoustomTransport = require('./CoustomTransport');
    const CustomLogger = require('./CustomLogger');
    module.exports = {
     get swLog() {
     return CustomLogger(this);
     }
    };

    调用

    // app/controller/home.js
    module.exports = app => {
     class HomeController extends app.Controller {
     async index() {
     this.ctx.swLog.info('Hello World');
     }
     }
     return HomeController;
    };

    结果

    [2018-11-02 19:25:09.665][226][/] Hello World

    到此,我们就能完整的自定义请求级别的日志了。

    总结

    下载本文
    显示全文
    专题