log4j 配置文件_日志框架Log4j源码解析(2)

e0cda0e2dfd1539249b0c3adcce03013.png

上一篇Log4j源码解析我们分析了Logger、Appender。相信读者应该已经搞清楚了这两者的对应关系,一个是面向用户(Logger),一个是面向输出(控制台、文件、远程)。今天我们来看看根据配置文件进行初始化。首先我们先看一段Log4j的配置。

log4j.rootLogger=debug,stdout,info,debug,warn,error
#log4j.threshold=fatal
#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n

log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.debug.File=./log/debug.log
log4j.appender.debug.Append=true
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.layout=org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.warn.File=./log/warn.log
log4j.appender.warn.Append=true
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.layout=org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
#error
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.error.File = ./log/error.log
log4j.appender.error.Append = true
log4j.appender.error.Threshold = ERROR
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

log4j.logger.org.apache.log4j.LogManager=error,test
log4j.appender.test = com.erely.appender.MyAppender
log4j.appender.test.Append=true
log4j.appender.test.layout = org.apache.log4j.PatternLayout
log4j.appender.test.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
log4j.appender.test.filter.MyFilter=com.erely.filter.MyFilter

我们先简单分析一下配置文件,首先是log4j.rootLogger节点。这个节点也是一个Logger,是所有Logger的父亲,如果一个Logger没有配置Appender就会默认继承父类的Appender,根节点的值的第一个是Logger的日志级别,后面的就是定义的一些Appender,比如info

log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.info.File=./log/info.log
log4j.appender.info.Append=true
log4j.appender.info.Threshold=INFO
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

这里边就定义了实现类,对应的文件、格式化、格式化正则表达等内容。在加载的时候应该是先找到rootLogger设置级别然后添加对应的Appender。可能我们要对某个类打日志单独处理。可以看到最后面加了一个LogManager的类的Logger,和rootLogger一样理解就可以,应该是比较简单的。接下来我们就通过源码来分析。在Log4j中获取Logger是通过LogManager来获取的。调用这个类的使用,该类的静态方法对配置文件进行了解析。在分析这些代码之前我们先看一看LoggerRepository接口的实现类Hierarchy

4fde9574d99fc5c4c44d1304a757337c.png

这个类还实现了RendererSupport、ThrowableRendererSupport,这里对这个两个我们不细说。我们看看上面列出来的成员变量。

private 

根据上面的变量相信读者已经能猜出来Hierarchy的功能。解析的时候会记录根Logger然后把其他的Logger放到ht中,另外设置日志一个日志的阈值(大于该阈值的级别才会打印)。这个里面有个设计就是emittedNoAppenderWarning这个是来警告在输出日志的时候没有找到Appender的但是这个至警告一次。笔者觉得作者认为如果多次警告就会占用系统资源,会降低性能。这个类中大部分方法都是set,get笔者对这些功能就不做介绍。我们来看看获取Logger的方法

public 

上面的代码应该很好理解,先根据name创建唯一的Key这个Key是由name和对应的hashCode组成的用来唯一表示某个Logger。然后就是通过名称获取Logger,如果没有获取到就创建,并设置一些属性。最后要设置对应的父Logger。我们来看看对应的方法

final 

上面是设置父Logger的过程,将报名一级一级往前拆出获取父类,如果父类存在并且是Logger则设置当前Logger。如果没有则先使用ProvisionNode进行占位。如果后面加载到了父Logger同时更新自己的信息和子类信息。如果都没有找到会设置父Logger为root。其他的方法这里就不介绍,遇到了就拿出来介绍下。我们继续看容器类LogManager。

static 

成员变量主要是配置文件名称。这里官网只留下了log4j.xml。其他的字段都标记过时了。但是笔者分析的话还是继续properties文件分析。下面我们看看静态代码块,这个方法里面就是在找配置文件,前面有很多代码都是去做检查这里笔者不介绍。

static 

找到配置文件进行处理配置文件,根据不同的配置文件类型使用不同的加载器。这里就使用了策略模式,根据不同的处理情况使用不同的处理器进行处理。

static
  

下面我们介绍真正的解析。可以看到上面用了很多方法去处理配置文件,首先是获取配置文件类型。然后是根据配置文件类型判断使用哪个加载器去加载。最后在使用加载器对配置实际的加载。这是遵守“单一职责”原则。我们看看具体加载

public
  

这个类里面首先处理的是内部日志的打印,开发者可以设置对应的属性值,设置内部日志开关,内部日志阈值等等。处理完毕内部日志之后。在处理用户的日志配置内容。笔者觉得这一块很妙,将系统态和用户态隔离开,读者可以思考下是不是感觉很妙。我们再看看解析根节点的方法。

void 

上面通过根节点的属性获取对应的值然后进行处理,上面的代码从读者应该可以发现,原来根节点可以使用两种属性名。继续看代码

void 

先对属性值进行,分割第一个是日志级别,设置根Logger的日志级别,然后后面的就是关联的Appender,循环的去处理。我们来看一下如何处理Appender的

Appender 

上面的代码中处理流程为,先使用log.append.+name拼接Appender的前缀,log.append.+name+.Layout.拼接布局的前缀,先获取Appender的实现类。创建实例,在通过获取到布局的类名创建实例,设置Appender的布局,同样获取错误处理器并设置。到这里一个大概的Appedner已经查不多了,但是我们发现还有一些属性没有设置到实例中呢。log4j会遍历所有的属性值,根据不同的前缀,获取到对应的属性名。然后获取属性的set方法。通过反射的方式将属性设置到实例中。处理完这些之后Appender就设置Filters了。然后在将appender放入一个Hashtable中(这个Hashtable是临时的,整个配置文件加载完毕之后会清除)。然后返回这个Appender。我们在回到解析根节点会将Appender加入到Logger中。

我们再看来解析工厂类的方法这个方法比较简单就是根据配置文件获取到对应的类名然后创建

protected 

到这里我们已经分析完了大部分包括rootLogger创建和处理Appender、处理Layout、Filter、错误处理等。最后一个处理是处理用户其他的Logger。

protected
  

这里处理的主要是三个一个是Logger,一个是自定义的消息处理,还有一个是异常。笔者这里只介绍Logger的处理。首先会根据前缀log4j.category.或log4j.logger.开始 获取到对应的Logger名称。然后调用和根节点一样的处理方法处理对应的Appender。处理完毕之后处理一个属性additivity。这个属性是代表子类是否使用父类的Appender。配置文件中的存在格式是log4j.additivity.+LoggerName。

上面提到消息实体,一般情况下我们打印日志的消息是String类型的,但是如果有一些特殊要求,当消息不再是String类型的而是某个自定义的类。那么我们要对这个消息进行处理就是上面log4j.renderer.来进行配置,最后还有一个是异常的这里笔者就不介绍了有兴趣的读者可以去看看源码。

到这里我们已经对整个加载过程做了分析,整体过程为

  1. 初始话Logger容器类,找到对应的配置文件。
  2. 根据配置文件的类型使用不同的处理器去处理配置文件。
  3. 先处理内部日志打印相关信息,然后在处理用户日志相关信息
  4. 处理rootLogger,主要包括设置日志级别、处理相关联的Appender
  5. 处理日志工厂(功能是创建Logger)。
  6. 处理另外的属性包括用户定义的Logger、自定义消息处理器Render、异常等。

如果对文章和作者感兴趣,可以关注作者的个人公众号。里面有更多的源码分析。


7b0a86e75d398492eee3715101611632.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值