logback-RollingFileAppender源码分析(关于缓冲和性能)
打印性能
是日志打印框架的核心关注点之一,从logback的日志打印流程看看其性能如何?
本文基于logback1.2.3
版本,以下配置(1.2.0版本后,logback提供了 SizeAndTimeBasedRollingPolicy
策略,可同时基于时间
+空间
两个维度控制日志文件的滚动)
形如下面这句debug日志的打印,调用栈是这样的
LOGGER.debug("msg {}", msg);
Appender接口&实现类
Appender
接口作为logback的核心三大件(Logger、Appender、Layout)之一,所有的日志打印Appender类都必须遵循Appender接口的规范。
关于性能的代码
根据上文的调用栈,可以看到Logger.debug()
最终调用了OutputStreamAppender.subAppend()
,其中核心代码就两句:encode
和writeBytes
/*** Actual writing occurs here.* <p>* Most subclasses of <code>WriterAppender</code> will need to override this* method.* * @since 0.9.0*/
protected void subAppend(E event) {if (!isStarted()) {return;}try {// this step avoids LBCLASSIC-139if (event instanceof DeferredProcessingAware) {((DeferredProcessingAware) event).prepareForDeferredProcessing();}// the synchronization prevents the OutputStream from being closed while we// are writing. It also prevents multiple threads from entering the same// converter. Converters assume that they are in a synchronized block.// lock.lock();byte[] byteArray = this.encoder.encode(event);writeBytes(byteArray); // 写入encode的日志内容} catch (IOException ioe) {// as soon as an exception occurs, move to non-started state// and add a single ErrorStatus to the SM.this.started = false;addStatus(new ErrorStatus("IO failure in appender", this, ioe));}
}
官方注释:OutputStream
的输出需要同步
,同时为了防止多个线程同时使用,converter
对event对象向输出流数据的转换需要得到串行化保障。
再看下writeBytes()
的实现,可以看到所有的日志打印线程到这个方法中需要阻塞、同步执行
write方法。
// OutputStreamAppender line: 54,默认立即刷新=true
boolean immediateFlush = true;/*** All synchronization in this class is done via the lock object.*/
protected final ReentrantLock lock = new ReentrantLock(false);private void writeBytes(byte[] byteArray) throws IOException {if(byteArray == null || byteArray.length == 0)return;lock.lock(); // 重入锁try {this.outputStream.write(byteArray);if (immediateFlush) { // 是否立即刷新缓冲内容this.outputStream.flush();}} finally {lock.unlock();}
}
其次需要关注immediateFlush
变量,因为我们没有配置,默认是开启
立即刷新。
官方注释:1.2.0版本后,支持在logback.xml的<appender>
节点设置immediateFlush
属性,如果不设置,默认值是true
,即每次日志输出流缓冲队列都将被刷新。
同时提醒,开启立即刷新,虽然比较“安全”
,但将降低
系统日志输出的整体性能
。
/*** Sets the immediateFlush option. The default value for immediateFlush is 'true'. If set to true,* the doEncode() method will immediately flush the underlying OutputStream. Although immediate flushing* is safer, it also significantly degrades logging throughput.** @since 1.0.3*/
public void setImmediateFlush(boolean immediateFlush) {addWarn("As of version 1.2.0 \"immediateFlush\" property should be set within the enclosing Appender.");addWarn("Please move \"immediateFlush\" property into the enclosing appender.");this.immediateFlush = immediateFlush;
}
为了确认缓冲
是真实存在的,就要看看这里this.outputStream
的具体实现类:ch.qos.logback.core.recovery.ResilientFileOutputStream
,其中组合
了一个os(BufferedOutputStream)
对象
// FileAppender中定义了缓冲区大小,默认是 8192 字节(8kb)
public static final long DEFAULT_BUFFER_SIZE = 8192;
private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE);// 构造方法,组合BufferedOutputStream
public ResilientFileOutputStream(File file, boolean append, long bufferSize) throws FileNotFoundException {this.file = file;fos = new FileOutputStream(file, append);this.os = new BufferedOutputStream(fos, (int) bufferSize);this.presumedClean = true;
}// 抽象父类默认方法,调用os.write
public void write(byte b[], int off, int len) {if (isPresumedInError()) {if (!recoveryCoordinator.isTooSoon()) {attemptRecovery();}return; // return regardless of the success of the recovery attempt}try {os.write(b, off, len);postSuccessfulWrite();} catch (IOException e) {postIOFailure(e);}
}
至此我们已经知道 RollingFileAppender
的日志打印最终会调用BufferedOutpuStream.write
方法,那么说明日志打印存在缓冲,且缓冲队列的默认是8kb
。
在FileAppender
有bufferSize
字段,找到了其setter方法,没有其他类调用,猜测是可以通过反射配置的。
public void setBufferSize(FileSize bufferSize) {addInfo("Setting bufferSize to ["+bufferSize.toString()+"]");this.bufferSize = bufferSize;
}
那我们能显示的设置FileAppender#bufferSize
大小吗?我做了以下尝试:
1.Logback官网查找Appender目录中的bufferSize设置,无果
2.项目中直接配置 <bufferSize>1MB</bufferSize>
没有起作用
3.stackoverflow搜索无果
总结
1.对于同步的RollingFileAppender
而言,多线程
的log appender打印是有意义的,因为只会在调用BufferedOutpuStream.write
方法时加锁同步,encode等方法也是比较耗时的,多线程可以增加系统吞吐量;对于 AsyncAppender
而言,增加日志消费线程
则意义不大, 这应该也是AsyncAppender
中只提供了一个worker守护线程
的原因。
2.使用 RollingFileAppender
时,logback提供给我们<ImmediateFlush>
标签使用,设置为false时可获得更高的logging吞吐量。
3.FileAppender中的bufferSize字段无法从外部设置,默认大小是8kb
,在ImmediateFlush=false
的情况下满8kb
刷新一次缓冲区。
logback-RollingFileAppender源码分析(关于缓冲和性能)相关推荐
- 【Android 性能优化】应用启动优化 ( 阶段总结 | Trace 文件分析及解决方案 | 源码分析梳理 | 设置主题的方案总结 ) ★
文章目录 一. 常用的耗时方法优化方案 ( 重要 ) 二. 源码分析梳理 1. 应用启动时间计算相关源码分析 2. Launcher 应用中启动 Android 应用流程 三. 启动白屏解决方案 An ...
- Python3.5源码分析-List概述
Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的List对象 list对象是一个变长对象,在运 ...
- java bufferedwrite_Java BufferedWriter BufferedReader 源码分析
一:BufferedWriter 1.类功能简介: BufferedWriter.缓存字符输出流.他的功能是为传入的底层字符输出流提供缓存功能.同样当使用底层字符输出流向目的地中写入字符或者字符数组时 ...
- Spring Security 源码分析:Spring Security 授权过程
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...
- Spring Security源码分析八:Spring Security 退出
为什么80%的码农都做不了架构师?>>> Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spr ...
- Linux驱动修炼之道-SPI驱动框架源码分析(上)
Linux驱动修炼之道-SPI驱动框架源码分析(上) SPI协议是一种同步的串行数据连接标准,由摩托罗拉公司命名,可工作于全双工模式.相关通讯设备可工作于m/s模式.主设备发起数据帧,允许多个从设 ...
- java io源码解读_Java IO源码分析(五)——CharArrayReader 和 CharArrayWriter
简介 CharArrayReader 是字符数组的输入流,它和我们之前讲的ByteArrayInputStream十分类似,顾名思义,区别在于一个用于字符数组,一个用于字节数组,在Java中字符是16 ...
- FATFS文件系统框架及源码分析
FATFS是一个为小型嵌入式系统设计的通用FAT(File Allocation Table)文件系统模块.FatFs 的编写遵循ANSI C,并且完全与磁盘I/O层分开.因此,它独立(不依赖)于硬件 ...
- Zookeeper源码分析(二) ----- zookeeper日志
zookeeper源码分析系列文章: Zookeeper源码分析(一) ----- 源码运行环境搭建 原创博客,纯手敲,转载请注明出处,谢谢! 既然我们是要学习源码,那么如何高效地学习源代码呢?答案就 ...
- FatFsVersion0.01源码分析
FatFsVersion0.01源码分析 目录 一.API的函数功能简述 二.FATFS主要数据结构 1.FAT32文件系统的结构 2.FATFS主要数据结构 ① FATFS ② DIR ③ ...
最新文章
- Kotlin问题解决
- 1. 批量梯度下降法BGD 2. 随机梯度下降法SGD 3. 小批量梯度下降法MBGD
- C语言经典例35-字符串反转
- EndNote(二)之英文引文导入方式
- 怎么知道网站是用什么程序做的
- 如何安装Bit-Z IOS版APP
- js判断只能输入数字或小数点
- Spring实战 MethodInvokingJobDetailFactoryBean使用与分析
- python龟图_python学习turtle(龟图标状态)
- 船舶和计算机结合论文格式,近海船舶监控系统中航迹关联算法的计算机研究与实现...
- 非使用FindControl方法找到深层嵌套的控件
- python打印日历代码_带tkinter的日历(打印所选日期)
- 创建SQL Server索引的好工具
- Android 发送邮件信息,附带附件
- Tensorflow官方文档学习理解 (五)-卷积MNIST
- 双亲委派模型与 Flink 的类加载策略
- Android HttpClient用法
- bs结构管理系统 服务器多少钱,购买BS或CS架构的进销存软件哪个更划算
- Secure CRT 配色方案
- 英语背单词有用吗_学英语千万不要背单词 背单词有效吗