性能优化之异步日志

Posted Dreamer who

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了性能优化之异步日志相关的知识,希望对你有一定的参考价值。

          你听说过打印日志能把系统拖垮的情况吗。

          所以项目中一般打印日志会使用异步AsyncAppender打印日志。

          那么使用了AsyncAppender,会不会性能就好了,就不会阻塞业务流程了,会不会丢失日志呢,我们来看一下logback的实现

        

 

                                                                                 

          先看下官方文档的介绍AsyncAppender 会把处理的事件缓存到一个阻塞队列,默认情况下达到队列容量的80%的时候,会丢弃TRACE, DEBUG and INFO级别的事件

根据默认配置neverBlock=false,队列也会发生阻塞,所以设置true,虽然完全是非阻塞但会丢失日志,特别是error级别的日志

       我们看下源码如何实现的。

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;
    

根据实现:

protected boolean isDiscardable(ILoggingEvent event) 
        Level level = event.getLevel();
        return level.toInt() <= Level.INFO_INT;
    
 public static final int OFF_INT = Integer.MAX_VALUE;
    public static final int ERROR_INT = 40000;
    public static final int WARN_INT = 30000;
    public static final int INFO_INT = 20000;
    public static final int DEBUG_INT = 10000;
    public static final int TRACE_INT = 5000;
    public static final int ALL_INT = Integer.MIN_VALUE;

TRACE, DEBUG and INFO级别的事件,在容量默认到达80%的时候会丢弃。

事件放入阻塞队列的时候即调用put方法时会判断容量及是否可以丢弃条件的判断,如下:

 @Override
    protected void append(E eventObject) 
        if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) 
            return;
        
        preprocess(eventObject);
        put(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();
            
        
    

当事件真正放入队列,是调用阻塞方法put,还是非阻塞方法offer,是由neverBlock配置决定的,默认false,会阻塞,所以为了性能,我们可以设置为true,也会丢弃日志。

真正处理队列事件的线程是默无闻的后台(Daemon)消费线程,

Worker worker = new Worker();
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()

 

 事件的最终归宿AppenderAttachableImpl<E> aa。

 

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();
        
    

 

 

     异步打印日志是否非阻塞,是否会丢弃日志,是完全可以配置的,如果可以丢弃,依据日志的报警系统就不太准了,只能靠打点系统在系统内部aop的方式或其他方式打点上报了。

 

 

 

以上是关于性能优化之异步日志的主要内容,如果未能解决你的问题,请参考以下文章

性能优化之异步日志

iOS之性能优化·列表异步绘制

异步编程CompletableFuture实现高性能系统优化之请求合并

MySQL优化之二:My SQL Server性能优化

Android性能优化之使用线程池处理异步任务

MySQL性能优化InnoDB之日志文件