最新的 log4j (5.1.0)剖析开篇

Posted 前端新视野

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最新的 log4j (5.1.0)剖析开篇相关的知识,希望对你有一定的参考价值。

log4j 其实本身经历了多个版本的迭代,大部分老项目或者一些教程都是比较老的版本了,最新的版本已经到了 5.1.0


可以查看:https://registry.npmjs.org/log4js。当然,并不代表版本差异会出现很大的问题。


v3 的变化:

https://log4js-node.github.io/log4js-node/v3-changes.md


v2 的变化:

https://github.com/log4js-node/log4js-node/blob/master/v2-changes.md


API 使用其实比较简单,一开始呢,出现了:


最外层:


  • configure

  • getLogger


configure 里面:这两个后面我们会重点一个一个介绍和剖析


  • appenders

  • categories


基本的使用方法很简单,如下:

调用了顶层的 getLogger,没有传入参数


var log4js = require('log4js');var logger = log4js.getLogger();logger.debug("Noderjs debug messages in 新视野");


我们发现是不会产生任何日志,我们修改一下:


logger.level = 'debug';


设置了  level 为 debug,就输出了以下日志:


[2019-09-10T23:29:44.880] [DEBUG] default - Noderjs debug messages in 新视野


我们再修改一下:


var logger = log4js.getLogger('odin');


对应的日志变化了:


[2019-09-10T23:33:04.957] [DEBUG] odin - Noderjs debug messages in 新视野


我们对比一下,发现日志分几步:


[2019-09-10T23:33:04.957][DEBUG]odinNoderjs debug messages in 新视野


一共 4 个部分:

当前日期类型 ==> logger.debug 调用了 debug 方法getLogger 的参数真正的日志内容 ==> logger.debug 函数里面的


顺着上面,我们来解释一下:

为什么不设置 logger.debug 就不会出现任何日志?


原因就在顶层的 getLogger 函数内部:

function getLogger(category{ configure({ appenders: {  out: {  type: "stdout"       }     }, categories: {  default: {  appenders: ["out"],  level: "OFF"       }     } })}

默认情况下: level 对应的是 OFF。



level 其实也是 log4j 或者说日志框架里面一个比较核心的概念。

在 libs/levels.js 版本为最新版本 5.1.0,我们可以看到:


class Level { constructor(level, levelStr, colour) { this.level = level; this.levelStr = levelStr; this.colour = colour; } static getLevel(sArg, defaultLevel) {} static addLevels(customLevels) {} isEqualTo(otherLevel) {} isGreaterThanOrEqualTo(otherLevel) {} isLessThanOrEqualTo(otherLevel) {}}


内置的 level 分为以下:


  • ALL

  • TRACE

  • DEBUG

  • INFO

  • WARN

  • ERROR

  • FATAL

  • MARK

  • OFF


每一个都有对应的 value,由小到大

一开始定义了一个数组,key 是 levels

然后调用了 addLevels 函数,传入了下面这个对象:


Level.levels = [];Level.addLevels({ ALL: { value: Number.MIN_VALUE, colour: 'grey' }, TRACE: { value: 5000, colour: 'blue' }, DEBUG: { value: 10000, colour: 'cyan' }, INFO: { value: 20000, colour: 'green' }, WARN: { value: 30000, colour: 'yellow' }, ERROR: { value: 40000, colour: 'red' }, FATAL: { value: 50000, colour: 'magenta' }, MARK: { value: 9007199254740992, colour: 'grey' }, // 2^53 OFF: { value: Number.MAX_VALUE, colour: 'grey' }});


看看对应的 addLevels 方法:


static addLevels(customLevels) { if (customLevels) { const levels = Object.keys(customLevels); levels.forEach((l) => { const levelStr = l.toUpperCase(); Level[levelStr] = new Level( customLevels[l].value, levelStr, customLevels[l].colour ); const existingLevelIndex = Level.levels.findIndex(lvl => lvl.levelStr === levelStr); if (existingLevelIndex > -1) { Level.levels[existingLevelIndex] = Level[levelStr]; } else { Level.levels.push(Level[levelStr]); } }); Level.levels.sort((a, b) => a.level - b.level); }}


一步一步拆解:


函数:上面调用的  addLevels 就是在这里定义的,所以 customLevels 被赋值了那个大对象


static addLevels(customLevels) {}



循环:获取对象的所有 keys,这里的 levels 对应的便是:


levels ==> [ 'ALL','TRACE','DEBUG','INFO','WARN','ERROR','FATAL','MARK','OFF' ]



具有源码实现:

const levels = Object.keys(customLevels);levels.forEach((l) => {})


内部:


会进行 toUpperCase,然后把对应的 value 给 level


const levelStr = l.toUpperCase();new Level( customLevels[l].value, levelStr, customLevels[l].colour);


进过排序之后,


Level.levels.sort((a, b) => a.level - b.level);


Level.levels 变成了:


 [   Level { level5e-324levelStr'ALL'colour'grey' }, Level { level: 5000, levelStr: 'TRACE', colour: 'blue' }, Level { level: 10000, levelStr: 'DEBUG', colour: 'cyan' }, Level { level: 20000, levelStr: 'INFO', colour: 'green' }, Level { level: 30000, levelStr: 'WARN', colour: 'yellow' }, Level { level: 40000, levelStr: 'ERROR', colour: 'red' }, Level { level: 50000, levelStr: 'FATAL', colour: 'magenta' }, Level { level: 9007199254740992, levelStr: 'MARK', colour: 'grey' }, Level { level: 1.7976931348623157e+308, levelStr: 'OFF', colour: 'grey' } ]



所以:

level: 'MARK' 的会只打印 mark 的日志

level: 'FATAL' 的会只打印 fatal 和 mark 的日志



我们再看一段日志:


{"startTime":"2019-09-10T16:08:06.242Z","categoryName":"json-test","data":["this is just a test"],"level":{"level":20000,"levelStr":"INFO","colour":"green"},"context":{},"pid":2719}


相比上面的,日志格式首先有一个比较大的转变:json 格式


我们看一下对应的代码:

log4js.configure({ appenders: { out: {  type: 'stdout',  layout: { type: 'json', separator: ',' }     } }, categories: { default: { appenders: ['out'], level: 'info' } }});


这里的 layout 里面的 type 为 json:

我们通过顶层的 addLayout 函数,增加了新的类型 json:


log4js.addLayout('json', config => function (logEvent) { return JSON.stringify(logEvent) + config.separator;});


type 设置为:


  • stderr

  • stdout


核心点:


process.stdout.writeprocess.stderr.write


开篇就现在这里介绍把,后面还有哦,记得关注~

以上是关于最新的 log4j (5.1.0)剖析开篇的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码剖析开篇:什么是Spring?

冰河连夜复现了Log4j最新史诗级重大漏洞,含视频和完整案例代码,全网最全,赶快收藏吧

爬虫学习之webmagic源码剖析

Log4j最新高危漏洞

测试人员只能点点点?深度剖析测试人员如何变得更优秀

干货 | 在Angular2中像Log4j一样管理日志