AsyncAppender可能出现OOM问题
AsyncAppender会使用BlockingQueue缓存事件,默认缓存大小为256。
/**
* The default buffer size.
*/
public static final int DEFAULT_QUEUE_SIZE = 256;
int queueSize = DEFAULT_QUEUE_SIZE;
blockingQueue = new ArrayBlockingQueue<E>(queueSize);
如果设置了过大的queueSize可能会导致OOM的发生。
同时我们也要设置合适的discardingThreshold。
static final int UNDEFINED = -1;
int discardingThreshold = UNDEFINED;
discardingThreshold的默认值为queueSize的 1/5。
if (discardingThreshold == UNDEFINED)
discardingThreshold = queueSize / 5;
discardingThreshold的作用就是当缓存BlockingQueue所剩余的空间不足discardingThreshold值时,会丢弃日志级别为TRACE, DEBUG 及 INFO的事件。
@Override
protected void append(E eventObject) {
if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) {
return;
}
preprocess(eventObject);
put(eventObject);
}
private boolean isQueueBelowDiscardingThreshold() {
return (blockingQueue.remainingCapacity() < discardingThreshold);
}
/**
* 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;
}
LOSSY BY DEFAULT IF 80% FULL AsyncAppender buffers events in a BlockingQueue. A worker thread created byAsyncAppender
takes events from the head of the queue, and dispatches them to the single appender attached toAsyncAppender
. Note that by default, AsyncAppender
will drop events of level TRACE, DEBUG and INFO if its queue is 80% full. This strategy has an amazingly favorable effect on performance at the cost of event loss.
https://logback.qos.ch/manual/appenders.html
AsyncAppender可能出现丢失日志的问题
上述上谈到了discardingThreshold值的作用,及会丢弃日志级别为TRACE, DEBUG 及 INFO的事件。
如果BlockingQueue队列的大小设置的比较小(默认值就很小了已经),且discardingThreshold设置的值大于0或者默认值,就很容易丢弃丢弃日志级别为TRACE, DEBUG 及 INFO的事件。
如果不想日志丢失,设置discardingThreshold值为0,而且最好把BlockingQueue队列的大小queueSize值设置的大一点。
AsyncAppender可能出现阻塞的问题
AsyncAppender的异步机制的出现本来就是优化日志阻塞问题的,但是使用不当还是出现阻塞问题。
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();
}
}
}
neverBlockd的默认值为false,会调用BlockingQueue的阻塞方法put。
boolean neverBlock = false;
那么当BlockingQueue队列满的时候或剩余容量达到阈值discardingThreshold就会出现阻塞问题。
设置neverBlock为true,永不阻塞,但可能丢失日志。如果希望兼顾性能和丢日志问题,根据AsyncAppender可能出现丢失日志的问题,设置discardingThreshold值为0,而且把BlockingQueue队列的大小queueSize值设置的大一点。
小结
AsyncAppender配置避坑指南:
1、OOM问题;
2、丢失日志问题;
3、阻塞问题;