logback最佳实践
背景
在最近的一次项目性能优化过程中,通过火焰图工具发现logback占用CPU很多,因此有了这篇总结文章。
logback 同步 vs 异步
同步写日志一般配置如下:
1
|
<appender name="ORDER_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
从配置可以看出来,写日志到日志文件的操作由RollingFileAppender
完成,该类的继承结构如下:
从继承结构可以知道RollingFileAppender
继承了UnsynchronizedAppenderBase
,根据doc文档说明,RollingFileAppender
需要自己处理多线程同步的问题。 在内部它确实也自己做了同步。
RollingFileAppender
1
|
@Override
|
OutputStreamAppender.java
OutputStreamAppender.java
1
|
/**
|
LayoutWrappingEncoder.java
1
|
LayoutWrappingEncoder.java
|
通过上面的代码逻辑可以得出如下结论:
- RollingFileAppender 写日志自己实现了多线程同步。
- RollingFileAppender 写日志是直接接入到日志文件中的。
- RollingFileAppender 默认日志到文件后会立刻flush,保证日志不丢失。
- 因为是顺序写文件,速度还是很高的,但是应为每次都flush,这会影响性能。
为了防止写日志影响应用性能, 我们需要使用异步写日志的方式。
异步写日志一般配置如下:
1
|
<appender name ="ASYNC_ORDER_LOG" class= "ch.qos.logback.classic.AsyncAppender">
|
AsyncAppender
继承自 AsyncAppenderBase
。 AsyncAppenderBase
的doc文档有如下的描述:
AsyncAppenderBase的子类写日志是异步的方式,内部使用了 BlockingQueue(异步写日志就是一个生产者-消费者模式)。
BlockingQueue 的使用者负责在应用关闭时关闭BlockingQueue,来确保不丢失日志。
注意:不丢失日志,可以通过如下的方式实现:
1
|
<configuration debug="true">
|
或
1
|
Runtime.addShutdownHook(new Thread (() -> {
|
AsyncAppenderBase
代码分析:
1
|
public static final int DEFAULT_QUEUE_SIZE = 256;
|
小结
- 同步写日志而且immediateFlush=true的配置下,性能最差。原因是写磁盘导致其他线程等待时间过长,虽然是顺序写,但是毕竟是持久化数据到磁盘。当然了,SSD能好一点。
- 异步写在JVM突然crash的时候有丢失数据的风险,但是性能很高,原因在于避免了直接写磁盘带来的性能消耗。但是需要注意的是多线程操作同一个阻塞队列也会因为锁争用的问题影响性能。
a. 不同的模块配置不同的日志文件和Appender,能减少锁争用的问题。
b. 减少不必要的日志输出。
c. 增加阻塞队列的大小,在neverBlock=false
的情况下避免线程等待问题。
d. 多个Appender(SiftingAppender),底层还是写同一个文件。好处是减少了多线程在阻塞队列上的锁竞争问题。
SiftingAppender
SiftingAppender是logback根据mdc中的变量动态创建appender的代理,只要我们将一个线程号作为日志名分发器discriminator注入到SiftingAppender中,它就可以动态的为我们创建不同的appender,达到分线程的目的,配置方式举例如下:
<!-- 分线程输出源 -->
<appender name="frameworkthread" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator class="ThreadDiscriminator">
<key>threadName</key>
</discriminator>
<sift>
<appender name="FILE-${threadName}" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<Encoding>UTF-8</Encoding>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}[%c][%thread][%X{tradeNo}][%p]-%m%n</pattern>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>D:/test/threadlogs/${threadName}-%d{yyyy-MM-dd}.%i.log
</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
</appender>
</sift>
</appender>
参考资料
http://logback.qos.ch/manual/appenders.html
一次logback多线程调优的经历
原文链接:https://leokongwq.github.io/2019/12/14/logback-best-practise.html
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步