log4j解析(包含源码)

2019-07-01 11:30:06 浏览数 (1)

读前问题:

1:log4j的日志流程是怎么样的?

2:log4j的配置文件各项都是什么含义?特别是有些配置里面有包名到底有什么用?

3:log4j的输出都是什么含义?具体见log4j conversion pattern各格式含义

一。log4j全局说明

log4j采用logger,appender模式,其中logger含有层级信息,顶级logger为rootLogger,其他logger在在解析时会以.(英文点号)为分隔符

划分层级,在获取logger时名字决定了要获取的logger实例

二。log4j配置文件解析

解析流程:

代码语言:javascript复制
解析全局配置 => 解析log4j.rootLogger  => 解析log4j.appender.* =>解析logFactory => 解析log4j.logger.* => 解析renderers

2.1:解析全局配置

全局配置包含:

代码语言:javascript复制
log4j.debug=true : Defining this value makes log4j print log4j-internal debug statements to System.out.

log4j.reset=true : If property set to true, then hierarchy will be reset before configuration

log4j.threshold=INFO #全局级别设定,不超过这个级别的一律不输出

2.2:解析log4j.rootLogger

格式:log4j.rootLogger=priority,appenderName1,appenderName2

说明:priority是日志级别,包含由低到高DEBUG,INFO,WARN,ERROR,FATAL五个级别

appenderName是自己定义的名称,只要与后面的一致就可以了,标示了在这个logger下面的输出器

ps:根logger必须有,是所有其余logger的顶级parent,他自身parent为null

rootLogger的名称为root,默认的

2.3:解析log4j.appender.* 格式:

代码语言:javascript复制
log4j.appender.appenderName=appenderClass
log4j.appender.appenderName.threshold=priority
log4j.appender.appenderName.layout=layoutClass
log4j.appender.appenderName.layout.ConversionPattern=conversionPattern
log4j.appender.appenderName.filter.filterName=filterClass
log4j.appender.appenderName.errorhandler=errorhandlerClass
log4j.appender.appenderName.errorhandler.root-ref=[true|false]
log4j.appender.appenderName.errorhandler.logger-ref=loggerName

说明:上面只是列出了一部分,像filter跟errorhandler平常我们基本不会用到,其实只要记住log4j.appender.appenderName后面的配置都是appender的属性就可以了

譬如自己继承了AppenderSkeleton这个类,里面有一个attr的属性 可以用log4j.appender.appenderName.attr=anyValue

appender自身也有级别过滤日志是否输出,可以用log4j.appender.appenderName.threshold=ERROR 这样就设定这个appender只输出ERROR级别的日志

2.4:解析logFactory

格式:log4j.loggerFactory=loggerFactoryClass

就是讲log4j默认的日志工厂替换为自定义的,基本没有必要这么做

2.5:解析log4j.logger.*

格式:log4j.logger.loggerName=priority,appenderName1,appenderName2

说明:这个logger的解析流程与rootLogger的基本一致,不同点在于logger是有层级的

假设我们有这几个配置:log4j.logger.com.oschina.net=DEBUG,appenderName

log4j.logger.com.oschina.net.web.action=INFO,appenderName

log4j.logger.com=ERROR,appenderName

log4j解析规则为:

解析loggerName = log4j.logger.后面的内容,譬如loggerName为com.oschina.net.web.action

解析上下级关系:com.oschina.net.web.action的有效parent为com.oschina.net,

com.oschina.net的有效parent为com

com的有效parent为root

ps:这里所说为有效parent,实际上com.oschina.net的直接parent为com.oschina,再上级的parent才是com,这里log4j做了一个处理,将com.oschina这一级

设定为一个占位符ProvisionNode,没有任何的appender

2.6:解析renderers

格式:log4j.renderer.renderedClass=renderingClass #定制对象显示 使用renderingClass来render renderedClass

说明:renderer就是为了解决我们在直接打印对象的时候如何输出值的问题,而不是直接调用toString方法

三:log4j的日志打印规则

通常我们都是这样来获取一个logger实例:

代码语言:javascript复制
Logger logger = LoggerFactory.getLogger(ObjectClass.class);

这里面的获取逻辑为:

假设ObjectClass.class的全限定路径为com.oschina.net.web.action.controller,那么LoggerFactory会生成一个名称为com.oschina.net.web.action.controller的logger(会缓存起来,下次直接使用),并初始化他的parent,此时logger的层级关系为:

日志打印时会自底向上用各自的appender输出日志内容,贴一下代码:

代码语言:javascript复制
        for (Category c = this; c != null; c = c.parent) {
            // Protected against simultaneous call to addAppender, removeAppender,...
            synchronized (c) {
                if (c.aai != null) {
                    writes  = c.aai.appendLoopOnAppenders(event);
                }
                if (!c.additive) {
                    break;
                }
            }
        }

不过这里有一点需要说明,因为在日志打印的时候需要判断logger的threshold,但是生成的最下面的com.oschina.net.web.action.controller这个loggerthreshold为null,

log4j会自底向上找到第一个不为null的logger来判定是否可以输出当前日志,因此在logger级别设定的时候就需要注意parent的级别需要比child的级别低,不然很容易导致

日志文件中输出了2条或以上的日志信息

四。appender类图

由于Logger相关类只有两个(另一个是Category类),configure相关类和filter类继承相关都很简单,这里只贴一个自己做的appender相关的类图

在看到DailyRollingFileAppender时,看到里面判断时间使用的System.currentTimeMillis(),在高并发下场景下是很耗时的一个操作,因此自己写了一个

加缓存的DailyRollingFileAppenderWithCache实现,具体可见github

五。总结

总的来说已经熟悉了log4j的流程,对曾经的疑问也已经有了解答,欢迎大家留言交流。

0 人点赞