性能优化之异步日志

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

          所以项目中一般打印日志会使用异步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的方式或其他方式打点上报了。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Logback 是一个 Java 日志框架,它是 log4j 的继任者。在进行 Logback性能优化时,可以采取以下步骤: 1. 合理配置日志级别:将日志级别设置为合适的水平,不要记录过多的低级别日志,以减少日志量和性能开销。尽量使用 DEBUG 级别仅在调试阶段启用。 2. 使用异步日志:启用异步日志可以减少主线程的阻塞时间,提高应用程序的性能。可以通过配置 `<appender>` 元素的 `async` 属性为 true 来启用异步日志。 3. 配置适当的日志输出格式:选择适合应用程序需求的日志输出格式,避免过多的额外信息和格式化操作。 4. 配置合适的日志文件大小和数量:通过设置 `<rollingPolicy>` 元素的 `MaxFileSize` 和 `MaxHistory` 属性,限制单个日志文件的大小和保留的历史文件数量,避免产生过大或过多的日志文件。 5. 使用合适的 Appender:选择适合应用程序的 Appender 类型,如使用 `FileAppender` 写入到文件,或使用 `ConsoleAppender` 输出到控制台。 6. 避免频繁地创建 Logger 对象:Logger 对象的创建开销较大,尽量避免在每个方法中都创建 Logger 对象,可以将 Logger 对象作为类的静态成员变量。 7. 避免使用过多的日志输出:日志输出的开销较大,尽量避免在循环或频繁调用的代码块中进行日志输出。 8. 使用条件日志:通过使用条件表达式判断日志是否需要输出,可以减少不必要的日志输出。 9. 合理配置日志框架本身的参数:根据应用程序的需求,配置 logback 的参数,如缓冲区大小、线程池大小等。 以上是一些常见的 Logback 性能优化方法,根据具体的应用场景和需求,还可以进行更细致的优化和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dreamer who

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值