Log4j2使用AsyncLogger的坑

      使用JAVA开发应用程序,没有不用log4j的,log4j配置化的集成方式,和简单的API,确实是JAVA日志模块的很好的解决方案。最近公司一个分布式项目需要开发原型,由我来负责其中的日志模块设计和开发。经过一番调研,打算使用log4j2,并使用其精华--异步logger来登记日志。所以在工程的classpath下新增了log4j2的属性文件,设定了全部logger 为异步logger,参数为Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector 

    先说下我们的开发环境:window 1CPU 双核、中间件有liberty、dubbo 、zookeeper;测试环境是SUSE Linux,4个CPU

    开发环境测试一切都很正常,liberty服务启动、运行和shutdown都没有问题。

    部署到测试环境之后,系统无法正常退出,当调用liberty的服务器停止命令server stop server1; 查看console.log,发现Log4j2在退出时,AsyncLoggerDisruptor的stop方法需要加载DiscardingAsyncQueueFullPolicy; 但其实这时候类初始化因为server1 shutdown之后,已经从内存回收了,所以系统报NullPointException。实际上Log4j2是还没有正常退出,但某些Logger对象的引用已经丢失了,这时候Log4j2再输出日志,会因为找不到Logger而再次报NPE(NullPointException);因为Log4j2的异常也需要输出到root Logger的,所以Log4j2写异常日志,报NPE,再异常,报NPE,如此循环,最后JVM报OOME,生成了Javacore和dump文件; 拿这两个文件分析,造成内存溢出的对象是Log4j2的message对象和RingBuffer。

    知道了导致错误的原因,怎么修复呢:

    1、Log4j2应该有很多人用,所以先在Log4j2的JIRA上将这个现象描述下,看看apache会不会针对这个问题做个优化:Log4j2 退出时报OOME

    2、Log4j2最大的优化就是异步Logger,所以打算一步一步退化。既然全量异步不行,可否混合同步和异步。而且仔细分析也发现,dubbo会定时发送心跳给zookeeper获取最新的服务注册信息,这部分的日志信息是输出到root logger了。分析root logger的日志输出,发现在停止服务器的时候,zookeeper unregister 日志信息在server1停止服务之后才输出,这是不应该的。所以root Logger 改成同步Logger ,其他Logger为AsyncLogger。

    3、进过第二步,发现不稳定,时好时坏。分析配置文件,发现AsyncLogger的additivity的属性都是true,相当于所以日志最后都会输出给root logger;果断改成false;测试了下,稳定了。

    最后总结下优化Log4j2的配置方案:1) 混合异步和同步Logger;root logger 为同步,其它为异步;2)AsyncLogger 的additivity属性设置为false;

   最后的最后上传日志部分截图,共参考:

你可能感兴趣的:(java)