log4j2是apache在log4j的基础上,参考logback架构实现的一套新的日志系统(我感觉是apache害怕logback了)。 log4j2的官方文档上写着一些它的优点: 在拥有全部logback特性的情况下,还修复了一些隐藏问题 API 分离:现在log4j2也是门面模式使用日志,默认的日志实现是log4j2,当然你也可以用logback(应该没有人会这么做) 性能提升:log4j2包含下一代基于LMAX Disruptor library的异步logger,在多线程场景下,拥有18倍于log4j和logback的性能 多API支持:log4j2提供Log4j 1.2, SLF4J, Commons Logging and java.util.logging (JUL) 的API支持 避免锁定:使用Log4j2 API的应用程序始终可以选择使用任何符合SLF4J的库作为log4j-to-slf4j适配器的记录器实现 自动重新加载配置:与Logback一样,Log4j 2可以在修改时自动重新加载其配置。与Logback不同,它会在重新配置发生时不会丢失日志事件。 高级过滤: 与Logback一样,Log4j 2支持基于Log事件中的上下文数据,标记,正则表达式和其他组件进行过滤。 插件架构: Log4j使用插件模式配置组件。因此,您无需编写代码来创建和配置Appender,Layout,Pattern Converter等。Log4j自动识别插件并在配置引用它们时使用它们。 属性支持:您可以在配置中引用属性,Log4j将直接替换它们,或者Log4j将它们传递给将动态解析它们的底层组件。 Java 8 Lambda支持 自定义日志级别 产生垃圾少:在稳态日志记录期间,Log4j 2 在独立应用程序中是无垃圾的,在Web应用程序中是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应时间性能。 和应用server集成:版本2.10.0引入了一个模块log4j-appserver,以改进与Apache Tomcat和Eclipse Jetty的集成。 在这段逻辑中,LogManager优先通过配置文件”log4j2.component.properties”通过配置项”log4j2.loggerContextFactory”来获取LoggerContextFactory,如果用户做了对应的配置,通过newCheckedInstanceOf方法实例化LoggerContextFactory的对象,最终的实现方式为: 再看构造方法: 原来这里是为了让osgi可以阻止启动。 再回到logManager: 可以看到在加载完Provider之后,会做factory的绑定: 这里有一个getContext方法,跟进, 直接看start: 可以看到每一个configuration都是从ConfigurationFactory拿出来的,我们先看看这个类的getInstance看看: 这里可以看到ConfigurationFactory中利用了PluginManager来进行初始化,PluginManager会将ConfigurationFactory的子类加载进来,默认使用的XmlConfigurationFactory,JsonConfigurationFactory,YamlConfigurationFactory这三个子类,这里插件化加载暂时按下不表。 回到reconfigure这个方法,我们看到获取ConfigurationFactory实例之后会去调用getConfiguration方法: 这个方法最重要的步骤就是config.start,这才是真正做配置解析的 发现这里面有一个比较重要的方法constructHierarchy,跟进: 发现就是对刚刚获取的configuration进行解析,然后塞进正确的地方。回到start方法,可以看到昨晚配置之后就是开启logger和appender了。 异步 AsyncAppender 一路跟进 继续跟进: 这里实际打日志的方法居然是交给一个config去实现的。。。感觉有点奇怪。。跟进看看 接下来就是callAppender了,我们直接开始看AsyncAppender的append方法: 直接从AsyncLogger的logMessage看进去: 这里的逻辑很简单,就是将日志相关的信息转换成RingBufferLogEvent(RingBuffer是Disruptor的无所队列),然后将其发布到RingBuffer中。发布到RingBuffer中,那肯定也有消费逻辑。这时候有两种方式可以找到这个消费的逻辑。 找disruptor被使用的地方,然后查看,但是这样做会很容易迷惑 按照Log4j2的尿性,这种Logger都有对应的start方法,我们可以从start方法入手寻找 在start方法中,我们找到了一段代码: final RingBufferLogEventHandler[] handlers = {new RingBufferLogEventHandler}; disruptor.handleEventsWith(handlers); 直接看看这个RingBufferLogEventHandler的实现: 顺着接口找上去,发现一个接口: 这个方法就是实际打日志了,AsyncLogger看起来还是比较简单的,只是使用了一个Disruptor。 插件化 之前在很多代码里面都可以看到 final PluginManager manager = new PluginManager(CATEGORY); manager.collectPlugins(pluginPackages); 其实整个log4j2为了获得更好的扩展性,将自己的很多组件都做成了插件,然后在配置的时候去加载plugin。 跟进collectPlugins。 (不太重要的方法省略) 我们可以看到在process方法中,PluginProcessor会先收集所有的Plugin,然后在写入文件。这样做的好处就是可以省去反射时候的开销。 然后我又看了一下Plugin这个注解,发现它的RetentionPolicy是RUNTIME,一般来说PluginProcessor是搭配RetentionPolicy.SOURCE,CLASS使用的,而且既然你把自己的Plugin扫描之后写在文件中了,RetentionPolicy就没有必要是RUNTIME了吧,这个是一个很奇怪的地方。 小结 总算是把Log4j2的代码看完了,发现它的设计理念很值得借鉴,为了灵活性,所有的东西都设计成插件式。互联网技术日益发展,各种中间件层出不穷,而作为工程师的我们更需要做的是去思考代码与代码之间的关系,毫无疑问的是,解耦是最具有美感的关系。 |
|