Logger性能优化
最近排查线上问题,无意中发现了Logger堵塞的情况,排查的同时也做下总结,做个笔记,以防备用。
先上图,看下实际堵塞的情况
从图中可以清楚的看到标黄的都在 waiting to lock <0x000000054011c380> 这个锁,这个锁被标红线程持有,如果标红的线程处理业务逻辑不够快,哪其它线程就会一直处在 BLOCKED 中。
关于org.apache.logging.log4j.core.appender.OutputStreamManager的说明,可以查看官方文档 点击查看,关于Write重载的几个方法如下
都有排它锁,这样在一个线程持有的时候,如果因为业务逻辑复杂、或者网络延迟 等等 都有机会造成线上发生的堵塞情况。
下面说下自己整理的主要优化方案
- 升级版本
a) Logback
Logback与Log4j的对比可以看官方文档 点击查看,如果升级到Logback记得要开启异步功能,进一步提高性能,异步配置如下
<appender name ="async" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold >0</discardingThreshold>
<queueSize>512</queueSize>
<appender-ref ref ="file"/>
</appender>
l discardingThreshold
默认情况下,当BlockingQueue还有20%容量,他将丢弃TRACE、DEBUG和INFO级别的event,只保留WARN和ERROR级别的event。为了保持所有的events,设置该值为0。
l queueSize
BlockingQueue的最大容量,默认情况下,大小为256。
l appender-ref
关联的appender
b) Log4f2
是log4j的升级版,详细资料可以查看官方文档,点击查看,我比较喜欢功能就是支持lambda,延迟加载就是利用这个功能实现的
2. 延迟加载
延迟加载说直白点就是按需加载,只有在用到的时候触发。你是否在项目中经常这样写日志
logger.info("消息提示:"+e.getMessage()+",业务数据:"+e.getData()+",级别别:"+e.getLevel().getName());
或者这样
logger.info("消息提示:{},业务数据:{},级别别:{}", e.getMessage(), e.getData(), e.getLevel().getName());
不管你用上面两个示例中的哪种写法,运行的时候,不管设置的级别是多少,都会执行e的方法,如果有序列化的情况会更糟糕。理想情况是根据设置的日志级别,只在真正调用的时候再执行,这时可以用如下写法,做到按需索取日志
logger.info("消息提示:{},业务数据:{},级别别:{}", () -> e.getMessage(), () -> e.getData(), () -> e.getLevel().getName());
3. 根据环境设置不同的级别
根据程序运行的环境设置不同的日志级别,一般分为开发、测试、预上线、生产 环境,日志级别应该逐步提高,避免日志的IO频繁读写提高性能,建议设置为debug、info、warn、warn