logback异步输出日志深入分析

Posted 互联网后端架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了logback异步输出日志深入分析相关的知识,希望对你有一定的参考价值。

1,异步输出日志的配置

logback中的异步输出日志使用了AsyncAppender这个appender

配置方式如下:

AsyncAppender的父类是AsyncAppenderBase,用到的代码基本都在这个父类里面。


2,异步输出日志时会把信息放到BlockingQueue中


当执行logger.info()方法时,Logger里的源码是这样的:

Logger类:ch.qos.logback.classic.Logger

info方法:

logback异步输出日志深入分析

filterAndLog_0_Or3Plus方法:

logback异步输出日志深入分析

中间判断了一下日志级别,如果本日志级别比配置的级别低,就不打日志了。比如配置的日志级别是ERROR,但是这段代码的级别是INFO,这段日志就不打印了。
最后的buildLoggingEventAndAppend方法:

logback异步输出日志深入分析

在这一步封装了LoggingEvent对象,Logback后面对日志的处理基本都是以LoggingEvent对象为单位了。

最后的callAppenders方法的代码:

logback异步输出日志深入分析

这个方法调用了Logger的appendLoopOnAppenders方法

appendLoopOnAppenders方法:

logback异步输出日志深入分析

aai是AppenderAttachableImpl类的对象,这个类在ch.qos.logback.core.spi包下,是专门用来处理相关appender的,维护了appender的列表,并且提供appender的添加、删除等方法。
Logger相关的appender就是配置文件中的ch.qos.logback.classic.AsyncAppender类,另外配置文件中的ch.qos.logback.core.rolling.RollingFileAppender不算,他属于AsyncAppender,不属于Logger。


AppenderAttachableImpl类的appendLoopOnAppenders方法:

logback异步输出日志深入分析

执行了所有相关appender的doAppend方法,异步输出日志的appender是AsyncAppender


AsyncAppender的父类是AsyncAppenderBase,在ch.qos.logback.core包下

AsyncAppenderBase的父类是UnsynchronizedAppenderBase,也在ch.qos.logback.core包下,doAppend方法在这个类中:

logback异步输出日志深入分析

最后的this.append方法在该类中是没有实现的抽象方法,具体实现在他的子类AsyncAppenderBase中:

logback异步输出日志深入分析

然后append方法里的put方法:

logback异步输出日志深入分析

日志的内容会被放到AsyncAppenderBase里定义的一个BlockingQueue中,至此Logger.info的任务完成了。

所谓的异步输出日志就是Logger.info负责往Queue中放日志,再起个线程把Queue中的日志写到磁盘上。

3,从BlockingQueue中获取信息并写入到文件

在AsyncAppender的父类AsyncAppenderBase里面定义了一个叫Worker的内部类,这个类负责从BlockingQueue中取出信息并处理,Worker的定义如下:

logback异步输出日志深入分析

另外AsyncAppenderBase还定义了Worker线程的start和stop方法,是重写的父类UnsynchronizedAppenderBase中的方法:

logback异步输出日志深入分析

可以看到,在while循环期间,Worker从blockingQueue里面拿出一个元素并进行处理,还是调用了AppenderAttachableImpl的appendLoopOnAppenders方法(和上面一样):

logback异步输出日志深入分析

调用了所有Appender的doAppend方法,在上面的配置中,AsyncAppenderBase相关的appender是RollingFileAppender,在ch.qos.logback.core.rolling包中

RollingFileAppender的父类是FileAppender,在ch.qos.logback.core包中

FileAppender的父类是OutputStreamAppender,也在ch.qos.logback.core包中

OutputStreamAppender的父类是UnsynchronizedAppenderBase,doAppend方法在这个类中(还是和上面的一样):

logback异步输出日志深入分析

这段代码,经过了一堆的判断和设置,调用了append方法,自己类里的append方法是个抽象方法,具体的实现写在了他的子类OutputStreamAppender里,代码如下:

logback异步输出日志深入分析

然后是subAppend方法:

logback异步输出日志深入分析

然后是writeOut方法:

logback异步输出日志深入分析

调用了encoder的doEncode方法,encoder的类是Encoder,Encoder是OutputStreamAppender定义的最终负责写日志的接口,由LayoutWrappingEncoder类实现:

LayoutWrappingEncoder的doEncode方法是这么写的:

用OutputStream写日志,immediateFlush可以在配置文件里配置,代表是否立即清空信息流,默认为true,如果配置为false,则会让outputStream快满的时候清空信息流。

至此日志写入完毕

 

4,一些可能有用的配置方式

1,blockingQueue长度。


blockingQueue长度决定了队列能放多少信息,在默认的配置下,如果blockingQueue放满了,后续想要输出日志的线程会被阻塞,直到Worker线程处理掉队列中的信息为止。根据实际情况适当调整队列长度,可以防止线程被阻塞。


2,immediateFlush=false。不立即清空输出流。


immediateFlush参数可以配置在<appender>里面,默认是true,代表是否立即刷新OutputStream中的信息。如果设置为false,会在OutputStream放满或隔断时间进行flush,具体由OutputStream类决定。据说设置为false之后输出日志的效率能提高为原来的4倍。

官网说:setting thisproperty to 'false' is likely to quadruple (your mileage may vary) loggingthroughput.


3,neverBlock=true。队列满了也不卡线程


neverBlock参数可以配置在<appender>里面,默认是false,代表在队列放满的情况下是否卡住线程。也就是说,如果配置neverBlock=true,当队列满了之后,后面阻塞的线程想要输出的消息就直接被丢弃,从而线程不会阻塞。这个配置用于线程很重要,不能卡顿,而且日志又不是很重要的场景,因为很有可能会丢日志。


4,自定义appender


开发者可以自己写一个appender类,需要继承AppenderBase<LoggingEvent>类并重写append(LoggingEventeventObject)方法,然后像别的appender一样配置到logback.xml里面,就可以定义自己的日志输出方式了。


以上是关于logback异步输出日志深入分析的主要内容,如果未能解决你的问题,请参考以下文章

Logback 配置文件这样优化,TPS提高 10 倍

日志 Logback 配置文件这么写,TPS 提高 10 倍

spring boot 性能测试工具汇总logback异步日志分析

spring boot 性能测试工具汇总logback异步日志分析

spring boot 性能测试工具汇总logback异步日志分析

Java Logback配置文件这么写,还愁不会整理日志?