一直對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次數。