异步输出logback日志的配置和源码研究

通常我们可以用springboot配置文件logback-spring.xml来配置日志的输出,简单来说,分为控制台、(info、warn、debug)、error日志。同步输出日志的配置文件如下所示

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <property resource="logback.properties"/>
    <!--ConsoleAppender代表是控制台日志-->
    <appender name="CONSOLE-LOG" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
        </layout>
    </appender>
    <!--过滤error级别的日志-->
    <!-- appender用来声明策略 -->
    <appender name="INFO-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <encoder>
            <pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
        </encoder>

        <!--滚动策略,TimeBasedRollingPolicy说明基于时间收集-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径-->
            <fileNamePattern>${LOG_PATH}/application.%d{yyyy-MM-dd-HH}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>
    <appender name="ERROR-LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>[%d{yyyy-MM-dd' 'HH:mm:ss.sss}] [%C] [%t] [%L] [%-5p] %m%n</pattern>
        </encoder>
        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.**TimeBasedRollingPolicy**">
            <!--路径-->
            <fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd-HH}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>
<!-- 用appender-ref来添加策略 -->
    <root level="info">
        <appender-ref ref="CONSOLE-LOG" />
        <appender-ref ref="INFO-LOG" />
        <appender-ref ref="ERROR-LOG" />
    </root>
</configuration>

修改为异步输出日志,在上面的配置基础上添加

<appender name="ASYNC-INFO" class="ch.qos.logback.classic.AsyncAppender">
       <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACE、DEBUG、INFO级别的日志 -->
       <discardingThreshold>0</discardingThreshold>
       <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
       <queueSize>512</queueSize>
        <neverBlock>true</neverBlock>
       <!-- 添加附加的appender,最多只能添加一个,这里就把之前声明的策略引入,然后修改成异步 -->
       <appender-ref ref="INFO-LOG"/>
   </appender>

   <appender name="ASYNC-ERROR" class="ch.qos.logback.classic.AsyncAppender">
       <!-- 不丢失日志.  如果走默认逻辑,如果队列的80%已满,则会丢弃trace、DEBUG、INFO级别的日志 -->
       <discardingThreshold>0</discardingThreshold>
       <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
       <queueSize>256</queueSize>
       <neverBlock>true</neverBlock>
       <!-- 添加附加的appender,最多只能添加一个 -->
       <appender-ref ref="ERROR-LOG"/>
   </appender>

discardingThreshold为什么默认80%,通过查看ch.qos.logback.core.AsyncAppenderBase的源码可以发现

public void start() {
        if (!this.isStarted()) {
            if (this.appenderCount == 0) {
                this.addError("No attached appenders found.");
            } else if (this.queueSize < 1) {
                this.addError("Invalid queue size [" + this.queueSize + "]");
            } else {
                this.blockingQueue = new ArrayBlockingQueue(this.queueSize);
                //默认值为-1,就走这个逻辑,改为队列大小的20%
                if (this.discardingThreshold == -1) {
                    **this.discardingThreshold = this.queueSize / 5;**
                }

                this.addInfo("Setting discardingThreshold to " + this.discardingThreshold);
                this.worker.setDaemon(true);
                this.worker.setName("AsyncAppender-Worker-" + this.getName());
                super.start();
                this.worker.start();
            }
        }
    }
// 添加日志
//this.isDiscardable默认为false,所以可以忽略这个逻辑,只看isQueueBelowDiscardingThreshold
protected void append(E eventObject) {
        if (!this.isQueueBelowDiscardingThreshold() || !this.isDiscardable(eventObject)) {
            this.preprocess(eventObject);
            this.put(eventObject);
        }
    }
// 队列剩余低于20%
    private boolean isQueueBelowDiscardingThreshold() {
        return this.blockingQueue.remainingCapacity() < this.discardingThreshold;
    }

因此如果不想丢弃日志,discardingThreshold需要设置为0。

还有一个要点,队列的put方法,将事件对象put进blockQueue,offer是非阻塞的,putUninterruptibly使用的是blockingQueue.put方法,是阻塞的。如果队列满了,使用offer就会返回false(丢日志),而使用put就会等待队列有位置。

private void put(E eventObject) {
        if (this.neverBlock) {
            this.blockingQueue.offer(eventObject);
        } else {
            this.putUninterruptibly(eventObject);
        }

    }

因此如果想要实现非阻塞,还是会丢失一些日志的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值