logback的AsyncAppender高效日志处理方式源码解析

 更新时间:2023年10月29日 08:31:59   作者:codecraft  
这篇文章主要为大家介绍了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,是的话则执行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,实现了AppenderAttachable接口,它定义了queueSize、discardingThreshold、neverBlock等属性,其start方法会根据queueSize创建ArrayBlockingQueue,discardingThreshold默认为queueSize / 5,之后启动Wroker;stop方法则执行worker.interrupt(),然后等待maxFlushTime让log进行flush;其append方法会先判断isQueueBelowDiscardingThreshold及isDiscardable,都为true则直接返回,否则执行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方法会不断循环从blockingQueue阻塞取出原生,然后添加到AppenderAttachableImpl;在started为false的时候跳槽循环,然后遍历blockingQueue,添加到AppenderAttachableImpl,然后将其从blockingQueue;最后执行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执行doAppend方法;其detachAndStopAllAppenders则遍历appenderList,挨个执行stop,最后clear掉整个appenderList

小结

logback的AsyncAppender使用ArrayBlockingQueue(默认size为256)来进行缓冲,每次append的时候会先判断isQueueBelowDiscardingThreshold及isDiscardable,为true则直接返回/丢弃,之后执行preprocess,最后执行put,put的时候有个参数neverBlock,为true则使用的是offer方法,队列满的时候会被丢弃,为false则是阻塞的方法,等到put成功才返回;另外它有个worker线程,不断从blockingQueue阻塞take元素出来然后写入到appenderList,在关闭时还会遍历队列写入到appenderList然后从队列移除,最后清空队列。

以上就是logback的AsyncAppender的详细内容,更多关于logback的AsyncAppender的资料请关注脚本之家其它相关文章!

相关文章

  • 调用Mybatis plus中的saveBatch方法报找不到表的问题

    调用Mybatis plus中的saveBatch方法报找不到表的问题

    在用Mybatis plus开发的项目中,用自带的API批量保存的方法saveBatch操作时,发现报没有找到表的错误,本文就来详细的介绍一下解决方法,感兴趣的可以了解一下
    2024-03-03
  • SpringRunner和SpringJUnit4ClassRunner的区别及说明

    SpringRunner和SpringJUnit4ClassRunner的区别及说明

    这篇文章主要介绍了SpringRunner和SpringJUnit4ClassRunner的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • springboot中事务管理@Transactional的注意事项与使用场景

    springboot中事务管理@Transactional的注意事项与使用场景

    今天小编就为大家分享一篇关于springboot中事务管理@Transactional的注意事项与使用场景,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • springmvc+Hibernate+JPA(混合事务)解读

    springmvc+Hibernate+JPA(混合事务)解读

    在Spring项目中,Spring Data JPA作为一种持久层框架,因其简化数据库操作而受到青睐,但在将其引入使用Hibernate的旧项目时,可能会遇到事务处理问题,解决方案包括配置两种事务管理器:Hibernate事务管理器和JPA事务管理器
    2024-09-09
  • java不用循环语句打印数组元素的实例

    java不用循环语句打印数组元素的实例

    下面小编就为大家带来一篇java不用循环语句打印数组元素的实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • java多线程-读写锁原理

    java多线程-读写锁原理

    本文主要介绍java多线程的知识,这里整理了相关资料及简单示例代码,有兴趣的小伙伴可以参考下
    2016-09-09
  • mybatisplus实现自动填充时间的项目实践

    mybatisplus实现自动填充时间的项目实践

    在数据库操作中,频繁设置创建时间和更新时间字段非常繁琐,通过使用MyBatis-Plus的自动填充功能,可以简化操作,本文就来详细的介绍一下,感兴趣的可以了解一下
    2024-10-10
  • Spring Boot Security配置教程

    Spring Boot Security配置教程

    在本文里我们给大家分享了关于Spring Boot Security配置的相关步骤以及注意要点,需要的朋友们跟着操作下。
    2019-05-05
  • SpringBoot处理跨域请求的四种方法

    SpringBoot处理跨域请求的四种方法

    在现代Web应用中,由于安全性和隐私的考虑,浏览器限制了从一个域向另一个域发起的跨域HTTP请求,解决这个问题的一种常见方式是实现跨域资源共享(CORS),SpringBoot提供了多种方式来处理跨域请求,本文将介绍其中的几种方法,感兴趣的朋友可以参考下
    2023-12-12
  • java 删除数组元素与删除重复数组元素的代码

    java 删除数组元素与删除重复数组元素的代码

    在java中删除数组元素与过滤重复数组元素我们都会需要去遍历数组然后根据我们设置的值或方法进行去除数组
    2013-10-10

最新评论