目录
Appender的设计思路
logback将日志事件的输出委托给的组件称为appender
,appender都必须实现ch.qos.logback.core.Appender
接口,Appender重要的接口方法如下:
package ch.qos.logback.core;
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.FilterAttachable;
import ch.qos.logback.core.spi.LifeCycle;
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {
public String getName();
public void setName(String name);
void doAppend(E event);
}
Appender接口扩展了FilterAttachable接口。因此,可以将一个或多个过滤器附加到Appender实例。
Appender最终负责输出日志记录事件。但是,他们可以将事件的实际格式委托给Layout或Encoder对象。每个布局/编码器都与一个且只有一个Appender相关联。
AppenderBase
Appender的一个基础子类是AppenderBase
类,如下
public synchronized void doAppend(E eventObject) {
// prevent re-entry.
if (guard) {
return;
}
try {
guard = true;
if (!this.started) {
if (statusRepeatCount++ < ALLOWED_REPEATS) {
addStatus(new WarnStatus(
"Attempted to append to non started appender [" + name + "].",this));
}
return;
}
if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
return;
}
// ok, we now invoke the derived class's implementation of append
this.append(eventObject);
} finally {
guard = false;
}
}
doAppend()方法要做的第一件事是检查防护措施是否设置为true。如果是,它将立即退出。如果未设置防护,则在下一条语句中将其设置为true。防护措施确保doAppend()方法不会递归调用自身。试想一下,如果append()方法内部又调用到doAppend()方法,会导致无限循环和堆栈溢出。
在下面的语句中,我们检查启动字段是否为true。如果不是,则doAppend()将发送警告消息并返回。换句话说,一旦关闭了附加器,就无法对其进行写入。 Appender对象实现LifeCycle接口,这意味着它们实现了start(),stop()和isStarted()方法。设置附加程序的所有属性后,logback的配置框架Joran调用start()方法来向附加程序发出信号以激活其属性。根据其种类,如果缺少某些属性或由于各种属性之间的干扰,追加程序可能无法启动。例如,假设文件创建依赖于截断模式,则FileAppender在确定地知道Append选项的值之前,不能对其File选项的值起作用。显式激活步骤可确保在知道属性值后,附加程序对其属性进行操作。
如果追加器无法启动或已停止,则将通过logback的内部状态管理系统发出警告消息。进行几次尝试后,为了避免内部状态系统被相同警告消息的副本淹没,doAppend()方法将停止发出这些警告。
下一个if语句检查附加过滤器的结果。根据过滤器链做出的决定,事件可以被拒绝或明确接受。如果过滤器链没有做出决定,则默认情况下会接受事件。
然后,doAppend()方法调用派生类对append()方法的实现。此方法完成了将事件附加到适当设备的实际工作。
最后,释放防护,以允许其他线程随后调用doAppend()方法。
类图
ConsoleAppender
logback-Console.xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
java -Dlogback.configurationFile=logback-console.xml ConsoleAppenderUse
FileAppender
logback-fileAppender.xml
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>
<!-- set immediateFlush to false for much higher logging throughput -->
<immediateFlush>true</immediateFlush>
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
测试程序
public class FileAppenderUse {
private static final Logger logger = LoggerFactory.getLogger(ConsoleAppenderUse.class);
//设置系统属性logback.configurationFile指定配置文件
//java -Dlogback.configurationFile=logback-fileAppender.xml FileAppenderUse
public static void main(String[] args) {
logger.info("ConsoleAppenderUse");
}
}
与时间关联的日志文件名
<configuration>
<!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
the key "bySecond" into the logger context. This value will be
available to all subsequent configuration elements. -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- use the previously created timestamp to create a uniquely
named log file -->
<file>log-${bySecond}.txt</file>
<encoder>
<pattern>%logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
上面的日志中表明,解析配置文件的时候会生成一个日志文件,文件名跟解析时间息息相关。
timestamp标签还有一个可选timeReference="contextBirth"
属性可以设置,表示时间值取context
生成的时间,大多数场景下,这两种时间相差无几。
RollingFileAppender
rollingPolicy
主要控制归档策略, triggeringPolicy
主要控制归档时间。
RollingFileAppender必须包含一个rollingPolicy
和一个triggeringPolicy
。
当然,如果RollingPolicy
已经实现了TriggeringPolicy
接口,那么只需要rollingPolicy
标签就够了。
TimeBasedRollingPolicy
看一个基于TimeBasedRollingPolicy
归档策略的例子
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
SizeAndTimeBasedRollingPolicy
看一个基于时间和单文件大小进行归档的例子
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
FixedWindowRollingPolicy
基于固定大小窗口的文件归档策略,只保留固定的日志文件个数。
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>