logback的AsyncAppender高效日志處理方式源碼解析
序
本文主要研究一下logback的AsyncAppender
AsyncAppender
ch/qos/logback/classic/AsyncAppender.java
public class AsyncAppender extends AsyncAppenderBase<ILoggingEvent> { boolean includeCallerData = false; /** * Events of level TRACE, DEBUG and INFO are deemed to be discardable. * @param event * @return true if the event is of level TRACE, DEBUG or INFO false otherwise. */ protected boolean isDiscardable(ILoggingEvent event) { Level level = event.getLevel(); return level.toInt() <= Level.INFO_INT; } protected void preprocess(ILoggingEvent eventObject) { eventObject.prepareForDeferredProcessing(); if (includeCallerData) eventObject.getCallerData(); } public boolean isIncludeCallerData() { return includeCallerData; } public void setIncludeCallerData(boolean includeCallerData) { this.includeCallerData = includeCallerData; } }
AsyncAppender繼承了AsyncAppenderBase,它新增了includeCallerData配置,另外覆蓋了isDiscardable、preprocess方法,isDiscardable針對TRACE、DEBUG的級別返回true,INFO返回false;preprocess則判斷是否includeCallerData,是的話則執(zhí)行eventObject.getCallerData()
AsyncAppenderBase
ch/qos/logback/core/AsyncAppenderBase.java
public class AsyncAppenderBase<E> extends UnsynchronizedAppenderBase<E> implements AppenderAttachable<E> { AppenderAttachableImpl<E> aai = new AppenderAttachableImpl<E>(); BlockingQueue<E> blockingQueue; /** * The default buffer size. */ public static final int DEFAULT_QUEUE_SIZE = 256; int queueSize = DEFAULT_QUEUE_SIZE; int appenderCount = 0; static final int UNDEFINED = -1; int discardingThreshold = UNDEFINED; boolean neverBlock = false; Worker worker = new Worker(); /** * The default maximum queue flush time allowed during appender stop. If the * worker takes longer than this time it will exit, discarding any remaining * items in the queue */ public static final int DEFAULT_MAX_FLUSH_TIME = 1000; int maxFlushTime = DEFAULT_MAX_FLUSH_TIME; /** * Is the eventObject passed as parameter discardable? The base class's implementation of this method always returns * 'false' but sub-classes may (and do) override this method. * <p/> * <p>Note that only if the buffer is nearly full are events discarded. Otherwise, when the buffer is "not full" * all events are logged. * * @param eventObject * @return - true if the event can be discarded, false otherwise */ protected boolean isDiscardable(E eventObject) { return false; } /** * Pre-process the event prior to queueing. The base class does no pre-processing but sub-classes can * override this behavior. * * @param eventObject */ protected void preprocess(E eventObject) { } @Override public void start() { if (isStarted()) return; if (appenderCount == 0) { addError("No attached appenders found."); return; } if (queueSize < 1) { addError("Invalid queue size [" + queueSize + "]"); return; } blockingQueue = new ArrayBlockingQueue<E>(queueSize); if (discardingThreshold == UNDEFINED) discardingThreshold = queueSize / 5; addInfo("Setting discardingThreshold to " + discardingThreshold); worker.setDaemon(true); worker.setName("AsyncAppender-Worker-" + getName()); // make sure this instance is marked as "started" before staring the worker Thread super.start(); worker.start(); } @Override public void stop() { if (!isStarted()) return; // mark this appender as stopped so that Worker can also processPriorToRemoval if it is invoking // aii.appendLoopOnAppenders // and sub-appenders consume the interruption super.stop(); // interrupt the worker thread so that it can terminate. Note that the interruption can be consumed // by sub-appenders worker.interrupt(); InterruptUtil interruptUtil = new InterruptUtil(context); try { interruptUtil.maskInterruptFlag(); worker.join(maxFlushTime); // check to see if the thread ended and if not add a warning message if (worker.isAlive()) { addWarn("Max queue flush timeout (" + maxFlushTime + " ms) exceeded. Approximately " + blockingQueue.size() + " queued events were possibly discarded."); } else { addInfo("Queue flush finished successfully within timeout."); } } catch (InterruptedException e) { int remaining = blockingQueue.size(); addError("Failed to join worker thread. " + remaining + " queued events may be discarded.", e); } finally { interruptUtil.unmaskInterruptFlag(); } } @Override protected void append(E eventObject) { if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) { return; } preprocess(eventObject); put(eventObject); } protected boolean isDiscardable(E eventObject) { return false; } protected void preprocess(E eventObject) { } private boolean isQueueBelowDiscardingThreshold() { return (blockingQueue.remainingCapacity() < discardingThreshold); } private void put(E eventObject) { if (neverBlock) { blockingQueue.offer(eventObject); } else { putUninterruptibly(eventObject); } } private void putUninterruptibly(E eventObject) { boolean interrupted = false; try { while (true) { try { blockingQueue.put(eventObject); break; } catch (InterruptedException e) { interrupted = true; } } } finally { if (interrupted) { Thread.currentThread().interrupt(); } } } //...... }
AsyncAppenderBase繼承了UnsynchronizedAppenderBase,實現(xiàn)了AppenderAttachable接口,它定義了queueSize、discardingThreshold、neverBlock等屬性,其start方法會根據(jù)queueSize創(chuàng)建ArrayBlockingQueue,discardingThreshold默認為queueSize / 5,之后啟動Wroker;stop方法則執(zhí)行worker.interrupt(),然后等待maxFlushTime讓log進行flush;其append方法會先判斷isQueueBelowDiscardingThreshold及isDiscardable,都為true則直接返回,否則執(zhí)行preprocess、put方法
Worker
ch/qos/logback/core/AsyncAppenderBase.java
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(); } }
Worker的run方法會不斷循環(huán)從blockingQueue阻塞取出原生,然后添加到AppenderAttachableImpl;在started為false的時候跳槽循環(huán),然后遍歷blockingQueue,添加到AppenderAttachableImpl,然后將其從blockingQueue;最后執(zhí)行detachAndStopAllAppenders
AppenderAttachableImpl
ch/qos/logback/core/spi/AppenderAttachableImpl.java
public int appendLoopOnAppenders(E e) { int size = 0; final Appender<E>[] appenderArray = appenderList.asTypedArray(); final int len = appenderArray.length; for (int i = 0; i < len; i++) { appenderArray[i].doAppend(e); size++; } return size; } /** * Remove and processPriorToRemoval all previously attached appenders. */ public void detachAndStopAllAppenders() { for (Appender<E> a : appenderList) { a.stop(); } appenderList.clear(); }
AppenderAttachableImpl的appendLoopOnAppenders方法會遍歷所有的appenderList執(zhí)行doAppend方法;其detachAndStopAllAppenders則遍歷appenderList,挨個執(zhí)行stop,最后clear掉整個appenderList
小結(jié)
logback的AsyncAppender使用ArrayBlockingQueue(默認size為256)來進行緩沖,每次append的時候會先判斷isQueueBelowDiscardingThreshold及isDiscardable,為true則直接返回/丟棄,之后執(zhí)行preprocess,最后執(zhí)行put,put的時候有個參數(shù)neverBlock,為true則使用的是offer方法,隊列滿的時候會被丟棄,為false則是阻塞的方法,等到put成功才返回;另外它有個worker線程,不斷從blockingQueue阻塞take元素出來然后寫入到appenderList,在關(guān)閉時還會遍歷隊列寫入到appenderList然后從隊列移除,最后清空隊列。
以上就是logback的AsyncAppender的詳細內(nèi)容,更多關(guān)于logback的AsyncAppender的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
調(diào)用Mybatis?plus中的saveBatch方法報找不到表的問題
在用Mybatis plus開發(fā)的項目中,用自帶的API批量保存的方法saveBatch操作時,發(fā)現(xiàn)報沒有找到表的錯誤,本文就來詳細的介紹一下解決方法,感興趣的可以了解一下2024-03-03SpringRunner和SpringJUnit4ClassRunner的區(qū)別及說明
這篇文章主要介紹了SpringRunner和SpringJUnit4ClassRunner的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04springboot中事務(wù)管理@Transactional的注意事項與使用場景
今天小編就為大家分享一篇關(guān)于springboot中事務(wù)管理@Transactional的注意事項與使用場景,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04springmvc+Hibernate+JPA(混合事務(wù))解讀
在Spring項目中,Spring Data JPA作為一種持久層框架,因其簡化數(shù)據(jù)庫操作而受到青睞,但在將其引入使用Hibernate的舊項目時,可能會遇到事務(wù)處理問題,解決方案包括配置兩種事務(wù)管理器:Hibernate事務(wù)管理器和JPA事務(wù)管理器2024-09-09java不用循環(huán)語句打印數(shù)組元素的實例
下面小編就為大家?guī)硪黄猨ava不用循環(huán)語句打印數(shù)組元素的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03mybatisplus實現(xiàn)自動填充時間的項目實踐
在數(shù)據(jù)庫操作中,頻繁設(shè)置創(chuàng)建時間和更新時間字段非常繁瑣,通過使用MyBatis-Plus的自動填充功能,可以簡化操作,本文就來詳細的介紹一下,感興趣的可以了解一下2024-10-10java 刪除數(shù)組元素與刪除重復(fù)數(shù)組元素的代碼
在java中刪除數(shù)組元素與過濾重復(fù)數(shù)組元素我們都會需要去遍歷數(shù)組然后根據(jù)我們設(shè)置的值或方法進行去除數(shù)組2013-10-10