关于自定义Egg.js的请求级别日志详解


Posted in Javascript onDecember 12, 2018

Egg.js 是什么?

Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。

注:Egg.js 缩写为 Egg

背景

组织为了更好的对各个业务的请求日志进行统一的分析,制定了统一的日志打印规范,比如:

[time][processId][traceId][userid] Hello World....

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

我们前端目前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][22896][/] Hello World

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
IE与Firefox在JavaScript上的7个不同写法小结
Sep 14 Javascript
javascript document.execCommand() 常用解析
Dec 14 Javascript
Prototype源码浅析 Enumerable部分(二)
Jan 18 Javascript
用JQuery 判断某个属性是否存在hasAttr的解决方法
Apr 26 Javascript
地址栏传递中文参数乱码在js里用escape转码
Aug 28 Javascript
详解JavaScript的回调函数
Nov 20 Javascript
实例讲解JavaScript中的this指向错误解决方法
Jun 13 Javascript
jQuery实现的省市县三级联动菜单效果完整实例
Aug 01 Javascript
ion content 滚动到底部会遮住一部分视图的快速解决方法
Sep 06 Javascript
基于mpvue小程序使用echarts画折线图的方法示例
Apr 24 Javascript
axios如何利用promise无痛刷新token的实现方法
Aug 27 Javascript
全面解析JavaScript Module模式
Jul 24 Javascript
JS/HTML5游戏常用算法之碰撞检测 像素检测算法实例详解
Dec 12 #Javascript
d3绘制基本的柱形图的实现代码
Dec 12 #Javascript
JS/HTML5游戏常用算法之碰撞检测 地图格子算法实例详解
Dec 12 #Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
Dec 12 #Javascript
js使用swiper实现层叠轮播效果实例代码
Dec 12 #Javascript
如何制作一个Node命令行图像识别工具
Dec 12 #Javascript
JS遍历JSON数组及获取JSON数组长度操作示例【测试可用】
Dec 12 #Javascript
You might like
jq的get传参数在utf-8中乱码问题的解决php版
2008/07/23 PHP
php使用unset()删除数组中某个单元(键)的方法
2015/02/17 PHP
php连接mysql数据库
2017/03/21 PHP
PHP使用数组实现矩阵数学运算的方法示例
2017/05/29 PHP
PHP实现通过CURL上传文件功能示例
2018/05/30 PHP
jQuery 隔行换色 支持键盘上下键,按Enter选定值
2009/08/02 Javascript
jquery应该如何来设置改变按钮input的onclick事件
2012/12/10 Javascript
JS实现点击网页判断是否安装app并打开否则跳转app store
2016/11/18 Javascript
基于JS实现二维码图片固定在右下角某处并跟随滚动条滚动
2017/02/08 Javascript
JavaScript正则表达式简单实用实例
2017/06/23 Javascript
Canvas放置反弹效果随机图形(实例)
2017/08/17 Javascript
Node.js微信 access_token ( jsapi_ticket ) 存取与刷新的示例
2017/09/30 Javascript
JavaScript类型相关的常用操作总结
2019/02/14 Javascript
解决qrcode.js生成二维码时必须定义一个空div的问题
2020/07/09 Javascript
JavaScript常用进制转换及位运算实例解析
2020/10/14 Javascript
js属性对象的hasOwnProperty方法的使用
2021/02/05 Javascript
[20:57]Ti4主赛事第三天开幕式
2014/07/21 DOTA
python基于mysql实现的简单队列以及跨进程锁实例详解
2014/07/07 Python
python控制台中实现进度条功能
2015/11/10 Python
Python使用修饰器执行函数的参数检查功能示例
2017/09/26 Python
浅谈flask中的before_request与after_request
2018/01/20 Python
python使用tkinter实现简单计算器
2018/01/30 Python
python2.7读取文件夹下所有文件名称及内容的方法
2018/02/24 Python
Python可迭代对象操作示例
2019/05/07 Python
python如何将多个PDF进行合并
2019/08/13 Python
python被修饰的函数消失问题解决(基于wraps函数)
2019/11/04 Python
Django对接支付宝实现支付宝充值金币功能示例
2019/12/17 Python
Python字符串格式化f-string多种功能实现
2020/05/07 Python
一加手机美国官方网站:OnePlus美国
2019/09/19 全球购物
静态变量和实例变量的区别
2015/07/07 面试题
营业员演讲稿
2013/12/30 职场文书
财政专业大学生职业生涯规划书
2014/09/17 职场文书
python 使用Tensorflow训练BP神经网络实现鸢尾花分类
2021/05/12 Python
JS数组去重详情
2021/11/07 Javascript
Python+Selenium实现读取网易邮箱验证码
2022/03/13 Python
MySQL解决Navicat设置默认字符串时的报错问题
2022/06/16 MySQL