通常我们可以用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);
}
}
因此如果想要实现非阻塞,还是会丢失一些日志的。