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針對(duì)TRACE、DEBUG的級(jí)別返回true,INFO返回false;preprocess則判斷是否includeCallerData,是的話(huà)則執(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,實(shí)現(xiàn)了AppenderAttachable接口,它定義了queueSize、discardingThreshold、neverBlock等屬性,其start方法會(huì)根據(jù)queueSize創(chuàng)建ArrayBlockingQueue,discardingThreshold默認(rèn)為queueSize / 5,之后啟動(dòng)Wroker;stop方法則執(zhí)行worker.interrupt(),然后等待maxFlushTime讓log進(jìn)行flush;其append方法會(huì)先判斷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ì)不斷循環(huán)從blockingQueue阻塞取出原生,然后添加到AppenderAttachableImpl;在started為false的時(shí)候跳槽循環(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方法會(huì)遍歷所有的appenderList執(zhí)行doAppend方法;其detachAndStopAllAppenders則遍歷appenderList,挨個(gè)執(zhí)行stop,最后clear掉整個(gè)appenderList
小結(jié)
logback的AsyncAppender使用ArrayBlockingQueue(默認(rèn)size為256)來(lái)進(jìn)行緩沖,每次append的時(shí)候會(huì)先判斷isQueueBelowDiscardingThreshold及isDiscardable,為true則直接返回/丟棄,之后執(zhí)行preprocess,最后執(zhí)行put,put的時(shí)候有個(gè)參數(shù)neverBlock,為true則使用的是offer方法,隊(duì)列滿(mǎn)的時(shí)候會(huì)被丟棄,為false則是阻塞的方法,等到put成功才返回;另外它有個(gè)worker線(xiàn)程,不斷從blockingQueue阻塞take元素出來(lái)然后寫(xiě)入到appenderList,在關(guān)閉時(shí)還會(huì)遍歷隊(duì)列寫(xiě)入到appenderList然后從隊(duì)列移除,最后清空隊(duì)列。
以上就是logback的AsyncAppender的詳細(xì)內(nèi)容,更多關(guān)于logback的AsyncAppender的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
調(diào)用Mybatis?plus中的saveBatch方法報(bào)找不到表的問(wèn)題
在用Mybatis plus開(kāi)發(fā)的項(xiàng)目中,用自帶的API批量保存的方法saveBatch操作時(shí),發(fā)現(xiàn)報(bào)沒(méi)有找到表的錯(cuò)誤,本文就來(lái)詳細(xì)的介紹一下解決方法,感興趣的可以了解一下2024-03-03
SpringRunner和SpringJUnit4ClassRunner的區(qū)別及說(shuō)明
這篇文章主要介紹了SpringRunner和SpringJUnit4ClassRunner的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
springboot中事務(wù)管理@Transactional的注意事項(xiàng)與使用場(chǎng)景
今天小編就為大家分享一篇關(guān)于springboot中事務(wù)管理@Transactional的注意事項(xiàng)與使用場(chǎng)景,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-04-04
springmvc+Hibernate+JPA(混合事務(wù))解讀
在Spring項(xiàng)目中,Spring Data JPA作為一種持久層框架,因其簡(jiǎn)化數(shù)據(jù)庫(kù)操作而受到青睞,但在將其引入使用Hibernate的舊項(xiàng)目時(shí),可能會(huì)遇到事務(wù)處理問(wèn)題,解決方案包括配置兩種事務(wù)管理器:Hibernate事務(wù)管理器和JPA事務(wù)管理器2024-09-09
java不用循環(huán)語(yǔ)句打印數(shù)組元素的實(shí)例
下面小編就為大家?guī)?lái)一篇java不用循環(huán)語(yǔ)句打印數(shù)組元素的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03
mybatisplus實(shí)現(xiàn)自動(dòng)填充時(shí)間的項(xiàng)目實(shí)踐
在數(shù)據(jù)庫(kù)操作中,頻繁設(shè)置創(chuàng)建時(shí)間和更新時(shí)間字段非常繁瑣,通過(guò)使用MyBatis-Plus的自動(dòng)填充功能,可以簡(jiǎn)化操作,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2024-10-10
java 刪除數(shù)組元素與刪除重復(fù)數(shù)組元素的代碼
在java中刪除數(shù)組元素與過(guò)濾重復(fù)數(shù)組元素我們都會(huì)需要去遍歷數(shù)組然后根據(jù)我們?cè)O(shè)置的值或方法進(jìn)行去除數(shù)組2013-10-10

