一直对logback异步输出日志误解为异步批量写入日志。
今天看了源代码。
首先logback的异步日志是如何配置的:
<!-- 管理端用户行为日志异步输出,异步的log片段必须在同步段后面,否则不起作用 --> <appender name="ASYNC_MANAGEMENT_HABITEVENT" class="ch.qos.logback.classic.AsyncAppender"> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <queueSize>10000</queueSize> <!-- 添加附加的appender,最多只能添加一个 --> <appender-ref ref="MANAGEMENT_HABITEVENT"/> </appender>
这里的MANAGEMENT_HABITEVENT就是普通的同步appender,可以理解为用异步包了一层。
我们来看这个AsyncAppender,你会发现代码很简单,然后你看到extends,扩展至AsyncAppenderBase,绝大部分逻辑都在这个base类中。进入这个类中第一眼就看到BlockingQueue<E> blockingQueue; 还有work工作线程。基本可以判定是个生产者消费者模式,logback使用ArrayBlockingQueue队列来暂存业务代码产生的日志。
再看消费者(work)的代码:
class Worker extends Thread { public void run() { AsyncAppenderBase<E> parent = AsyncAppenderBase.this; AppenderAttachableImpl<E> aai = parent.aai; // loop while the parent is started while (parent.isStarted()) { try { E e = parent.blockingQueue.take(); //单条循环 aai.appendLoopOnAppenders(e); } catch (InterruptedException ie) { break; } } addInfo("Worker thread will flush remaining events before exiting. "); for (E e : parent.blockingQueue) { aai.appendLoopOnAppenders(e); parent.blockingQueue.remove(e); } aai.detachAndStopAllAppenders(); } }
while单条循环!!!!
总结:logback异步输出日志使用了生产者消费者模型,并且是由一个消费者循环单条写入日志文件。
后话:个人理解,其实可以扩展下,修改为批量写入,减少IO次数。