log4j解析(包含源码)

本文详细解析了Log4j的日志配置过程及日志打印规则,包括全局配置、rootLogger、appender配置等,并介绍了logger的层级关系及其在日志输出中的作用。

读前问题:

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

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

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

一。log4j全局说明

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

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

二。log4j配置文件解析

    解析流程:

解析全局配置 => 解析log4j.rootLogger  => 解析log4j.appender.* =>解析logFactory => 解析log4j.logger.* => 解析renderers

 

    2.1:解析全局配置

    全局配置包含:

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.*
    格式:

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实例:

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输出日志内容,贴一下代码:

        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的流程,对曾经的疑问也已经有了解答,欢迎大家留言交流。

 

 

转载于:https://my.oschina.net/liufq/blog/872484

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值