关于自定义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 相关文章推荐
javascript十个最常用的自定义函数(中文版)
Sep 07 Javascript
动态的改变IFrame的高度实现IFrame自动伸展适应高度
Dec 28 Javascript
JavaScript获取图片的原始尺寸以宽度为例
May 04 Javascript
Java Mybatis框架入门基础教程
Sep 21 Javascript
深入浅析JavaScript系列(13):This? Yes,this!
Jan 05 Javascript
jquery 输入框查找关键字并提亮颜色的实例代码
Jan 23 jQuery
深入理解node.js http模块
Jan 24 Javascript
JS实现全屏预览F11功能的示例代码
Jul 23 Javascript
js实现自动播放匀速轮播图
Feb 06 Javascript
微信小程序实现转盘抽奖
Sep 21 Javascript
openlayers实现图标拖动获取坐标
Sep 25 Javascript
Python机器学习之决策树和随机森林
Jul 15 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
php中var_export与var_dump的区别分析
2010/08/21 PHP
php切割页面div内容的实现代码分享
2012/07/31 PHP
使用Discuz关键词服务器实现PHP中文分词
2014/03/11 PHP
PHP的命令行命令使用指南
2015/08/18 PHP
PHP编程求最大公约数与最小公倍数的方法示例
2017/05/29 PHP
一个对于js this关键字的问题
2007/01/09 Javascript
使Ext的Template可以解析二层的json数据的方法
2007/12/22 Javascript
VBS通过WMI监视注册表变动的代码
2011/10/27 Javascript
json原理分析及实例介绍
2012/11/29 Javascript
Javascript 鼠标移动上去小三角形滑块缓慢跟随效果
2013/04/26 Javascript
jQuery获取CSS样式中的颜色值的问题,不同浏览器格式不同的解决办法
2013/05/13 Javascript
动态载入js提高网页打开速度的方法
2014/07/04 Javascript
究竟什么是Node.js?Node.js有什么好处?
2015/05/29 Javascript
js+html5实现canvas绘制简单矩形的方法
2015/06/05 Javascript
JS实现可直接显示网页代码运行效果的HTML代码预览功能实例
2015/08/06 Javascript
js console.log打印对像与数组用法详解
2016/01/21 Javascript
Bootstrap页面布局基础知识全面解析
2016/06/13 Javascript
JS实现太极旋转思路分析
2016/12/09 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
2020/11/02 Javascript
[14:00]DOTA2国际邀请赛史上最长大战 赛后专访B神
2013/08/10 DOTA
linux 下实现python多版本安装实践
2014/11/18 Python
在Django的视图中使用form对象的方法
2015/07/18 Python
python+Django+apache的配置方法详解
2016/06/01 Python
python中利用Future对象回调别的函数示例代码
2017/09/07 Python
Python获取基金网站网页内容、使用BeautifulSoup库分析html操作示例
2019/06/04 Python
django框架CSRF防护原理与用法分析
2019/07/22 Python
Python requests设置代理的方法步骤
2020/02/23 Python
时尚圣经:The Fashion Bible
2019/03/03 全球购物
英国家居装饰品、户外家具和玻璃器皿购物网站:Rinkit.com
2019/11/04 全球购物
什么叫做SQL注入,如何防止
2016/10/04 面试题
护士自荐信
2013/10/25 职场文书
机械工程师的岗位职责
2013/11/17 职场文书
社会实践评语
2014/04/28 职场文书
学术诚信承诺书
2014/05/26 职场文书
学习张丽丽心得体会
2014/09/03 职场文书
小学四年级作文之最感动的一件事
2019/11/01 职场文书