来源:logback.qos.ch/manual/appe… 作者:Ceki Gülcü、Sébastien Pennec、Carl Harris 版权所有 © 2000-2022 QOS.ch Sarl
本文档使用 知识共享署名 - 非商业性使用 - 相同方式共享 2.5 许可协议 授权。
There is so much to tell about the Western country in that day that it is hard to know where to start. One thing sets off a hundred others. The problem is to decide which one to tell first.
有关这个西方国家的许多事情都值得讲述,但是从何说起又很难决定。一个问题引出一百个问题。问题在于决定先说哪一个。
——约翰·斯坦贝克,《伊甸园之东》
第 4 章:Appenders
为了运行本章中的示例,您需要确保某些 JAR 文件在类路径上存在。请参考 安装页面 以获取更多详细信息。
什么是 Appender?
Logback 将写入日志事件的任务委托给称为 appender 的组件。Appender 必须实现 ch.qos.logback.core.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 接口中的大多数方法都是设置器和获取器。一个值得注意的例外是 doAppend() 方法,它以类型为 E 的对象实例作为其唯一参数。E 的实际类型将根据 logback 模块的不同而变化。在 logback-classic 模块中,E 将是类型为 ILoggingEvent 的对象,而在 logback-access 模块中,E 将是类型为 AccessEvent 的对象。doAppend() 方法可能是 logback 框架中最重要的方法。它负责以适当的格式将日志事件输出到相应的输出设备。
Appender 是有名称的实体。这确保它们可以通过名称引用,这是配置脚本中的一种确认质量。Appender 接口扩展了 FilterAttachable 接口。因此,一个或多个过滤器可以附加到 appender 实例上。过滤器将在后续章节中详细讨论。
Appender 最终负责输出日志事件。然而,它们可能会将事件的实际格式化委托给 Layout 或 Encoder 对象。每个布局/编码器与一个且仅一个 appender 相关联,称为所属 appender。一些 appender 具有内置或固定的事件格式。因此,它们不需要也没有布局/编码器。例如,SocketAppender 只是在传输日志事件之前对其进行序列化。
AppenderBase
ch.qos.logback.core.AppenderBase 类是实现 Appender 接口的抽象类。它提供了所有 appender 都共享的基本功能,例如获取或设置其名称、激活状态、布局和过滤器的方法。它是 logback 随附的所有 appender 的超类。虽然是一个抽象类,但 AppenderBase 实际上在 Append 接口中实现了 doAppend() 方法。讨论 AppenderBase 类最清晰的方式可能是通过展示实际源代码的摘录。
public synchronized void doAppend(E eventObject) {
// 防止重入。
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;
}
// 好的,现在我们调用派生类的 append 实现
this.append(eventObject);
} finally {
guard = false;
}
}
该 doAppend() 方法的实现是同步的。由此可见,从不同线程记录到相同的 appender 是安全的。当一个线程,比如 T,正在执行 doAppend() 方法时,其他线程的后续调用会被排队,直到 T 离开 doAppend() 方法,确保 T 独占 appender。
由于这种同步并不总是适当的,logback 附带了 ch.qos.logback.core.UnsynchronizedAppenderBase,它与 AppenderBase 类非常相似。为了简洁起见,我们在本文档的其余部分将讨论 UnsynchronizedAppenderBase。
doAppend() 方法的第一件事是检查守卫是否设置为 true。如果是,它立即退出。如果守卫未设置,则在下一条语句中设置为 true。守卫确保 doAppend() 方法不会递归调用自身。想象一下,在 append() 方法之外的某个地方调用了一个组件,想要记录一些东西。它的调用可能被定向到刚刚调用它的同一个 appender,导致无限循环和堆栈溢出。
在接下来的语句中,我们检查 started 字段是否为 true。如果不是,doAppend() 将发出警告消息并返回。换句话说,一旦 appender 被关闭,就不可能向其写入数据。Appender 对象实现了 LifeCycle 接口,这意味着它们实现了 start()、stop() 和 isStarted() 方法。在设置完 appender 的所有属性之后,logback 的配置框架 Joran 调用 start() 方法,以通知 appender 激活其属性。根据其类型,如果缺少某些属性或因各种属性之间的干扰而无法启动,某个 appender 可能无法启动。例如,考虑到文件创建取决于截断模式,FileAppender 在不确定 Append 选项的值之前无法对其 File 选项的值进行操作。显式的激活步骤确保 appender 在其值变为已知之后再对其属性进行操作。
如果 appender 无法启动或已停止,doAppend() 方法将通过 logback 的内部状态管理系统发出警告消息。经过多次尝试,为了避免使内部状态系统充斥相同警告消息的副本,doAppend() 方法将停止发出这些警告。
接下来的 if 语句检查了附加过滤器的结果。根据过滤器链的决定,事件可以被拒绝或显式接受。如果过滤器链没有做出决定,事件将默认被接受。
doAppend() 方法然后调用派生类的 append() 方法的实现。这个方法实际上是将事件附加到适当的设备上的工作。
最后,守卫被释放,以允许后续调用 doAppend() 方法。
在本手册的其余部分,我们将保留术语 "option" 或者 "property",用于指代通过 JavaBeans 内省动态推断出的任何属性,通过 setter 和 getter 方法。
Logback-core
Logback-core 为其他 logback 模块构建打下了基础。一般来说,logback-core 中的组件需要进行一些,尽管很少量的自定义。然而,在接下来的几节中,我们将描述几个可以直接使用的 appender。
OutputStreamAppender
OutputStreamAppender 将事件追加到 java.io.OutputStream。这个类提供了其他 appender 基于的基本服务。通常情况下,用户不会直接实例化 OutputStreamAppender 对象,因为一般情况下 java.io.OutputStream 类型无法方便地映射到字符串,因为没有办法在配置脚本中指定目标 OutputStream 对象。简而言之,你无法从配置文件配置 OutputStreamAppender。然而,这并不意味着 OutputStreamAppender 缺少可配置的属性。下面描述了这些属性。
| 属性名称 | 类型 | 描述 |
|---|---|---|
encoder | Encoder | 决定事件被写入底层 OutputStreamAppender 的方式。编码器在 专门章节 中描述。 |
immediateFlush | boolean | immediateFlush 的默认值为 'true'。立即刷新输出流可确保日志事件立即被写出,以防止应用程序在不正确关闭 appender 的情况下退出而丢失日志事件。另一方面,将此属性设置为'false'可能会使日志吞吐量增加四倍(结果可能有所不同)。同样,如果 immediateFlush 设置为'false',并且在应用程序退出时未正确关闭 appender,那么尚未写入磁盘的日志事件可能会丢失。 |
OutputStreamAppender 是其他三个 appender 的超类,分别是 ConsoleAppender、FileAppender,后者又是 RollingFileAppender 的超类。下图显示了 OutputStreamAppender 及其子类的类图。
ConsoleAppender
ConsoleAppender,顾名思义,将事件追加到控制台,更确切地说是 System.out 或 System.err,前者是默认目标。ConsoleAppender 使用用户指定的编码器格式化事件。编码器将在后续章节中讨论。System.out 和 System.err 都是 java.io.PrintStream 类型。因此,它们被包装在一个缓冲 I/O 操作的 OutputStreamWriter 中。
| 属性名称 | 类型 | 描述 |
|---|---|---|
encoder | Encoder | 参见 OutputStreamAppender 的属性。 |
target | String | 字符串值之一为 System.out 或 System.err。默认目标是 System.out。 |
withJansi | boolean | 默认情况下 withJansi 属性被设置为 false。将 withJansi 设置为 true 会激活 Java 库,在 Windows 机器上提供对 ANSI 颜色代码的支持。在 Windows 主机上,如果此属性设置为 true,则应将 "org.fusesource.jansi:jansi:${jansi.version}" 放在类路径上。请注意,类 Unix 操作系统(如 Linux 和 Mac OS X)默认支持 ANSI 颜色代码。在 Eclipse IDE 下,您可能想尝试 ANSI in Eclipse Console 插件。 |
以下是一个使用 ConsoleAppender 的示例配置。
示例:ConsoleAppender 配置 (logback-examples/src/main/resources/chapters/appenders/conf/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} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
规范格式 (1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.ConsoleAppender"/>
<appender name="STDOUT" class="ConsoleAppender">
<encoder class="PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
在将当前路径设置为 logback-examples 目录并 设置类路径 后,您可以通过发出以下命令来测试上述配置文件:
java chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-Console.xml
文件追加器(FileAppender)
FileAppender 是 OutputStreamAppender 的子类,用于将日志事件追加到文件中。目标文件由 File 选项指定。如果文件已经存在,则根据追加属性的值进行追加或截断。
| 属性名称 | 类型 | 描述 |
|---|---|---|
append | boolean | 如果为 true,则事件追加到现有文件的末尾。否则,如果 append 为 false,则会截断任何现有文件。默认情况下,追加选项设置为 true。 |
encoder | Encoder | 参见 OutputStreamAppender 属性。 |
file | String | 要写入的文件的名称。如果文件不存在,将创建它。在 MS Windows 平台上,用户经常忘记转义反斜杠。例如,值"c:\\temp\\test.log" 可能无法正确解释为 "'\\t'" 是一个转义序列,被解释为单个制表符字符(\u0009)。可以将正确的值指定为 "c:/temp/test.log",或者作为 "c:\\\\temp\\\\test.log"。file 选项没有默认值。如果文件的父目录不存在, FileAppender 将自动创建它,包括任何必要但不存在的父目录。 |
bufferSize | FileSize | bufferSize 选项在 immediateFlush 选项设置为 false 的情况下设置输出缓冲区的大小。bufferSize 的默认值为 8192。即使在非常重载和持久的负载情况下,256KKB 的值似乎也足够。以“FileSize”为单位定义的选项可以通过将数字值与 KB、MB 和 GB 后缀一起指定为字节、千字节、兆字节或千兆字节。例如,5000000、5000KB、5MB 和 2GB 都是有效的值,前三个值相等。 |
prudent | boolean | 在谨慎模式下,FileAppender 将安全地写入指定的文件,即使在不同 JVM 中运行的其他 FileAppender 实例存在的情况下,这些实例可能在不同的主机上运行。谨慎模式的默认值为 false。谨慎模式可以与 RollingFileAppender 结合使用,但是存在一些 限制。谨慎模式意味着 append 属性会自动设置为 true。谨慎模式依赖于独占文件锁。实验证明,文件锁大约会使写入日志事件的成本增加三倍(x3)。在一个“平均”的个人电脑上,当谨慎模式关闭时,写入单个日志事件大约需要 10 微秒。当打开谨慎模式时,输出单个日志事件大约需要 30 微秒。这意味着在谨慎模式关闭时,每秒的日志吞吐量为 100,000 个事件,在谨慎模式下约为 33,000 个事件。 谨慎模式有效地对所有写入同一文件的 JVM 之间的 I/O 操作进行串行化。因此,随着竞争访问文件的 JVM 数量的增加,每个 I/O 操作所需的延迟也会增加。只要“总共”I/O 操作的数量约为每秒 20 个日志请求,对性能的影响应该可以忽略不计。生成每秒 100 个或更多 I/O 操作的应用程序可能会对性能产生影响,应避免使用谨慎模式。 ==网络文件锁== 当日志文件位于网络文件系统上时,谨慎模式的成本甚至更高。同样重要的是,网络文件系统上的文件锁有时可能严重偏向于当前拥有锁的进程在释放锁后立即重新获取锁。因此,当一个进程占用日志文件的锁时,其他进程会因等待锁而饿死,达到看起来死锁的程度。 谨慎模式的影响在很大程度上取决于网络速度以及操作系统的实现细节。我们提供了一个非常小的应用程序,名为 FileLockSimulator,它可以帮助您模拟在您的环境中使用谨慎模式的行为。 |
==立即刷新== 默认情况下,每个日志事件都会立即刷新到底层输出流。这种默认方法更安全,因为在应用程序在没有正确关闭追加器的情况下退出时,日志事件不会丢失。然而,为了显著增加日志吞吐量,可以将 immediateFlush 属性设置为 false。
以下是 FileAppender 的配置文件示例:
示例:FileAppender 配置(logback-examples/src/main/resources/chapters/appenders/conf/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} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
规范(1.3 版)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.FileAppender"/>
<appender name="FILE" class="FileAppender">
<file>testFile.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder class="PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
将当前目录更改为 logback-examples ,通过启动以下命令运行此示例:
java chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-fileAppender.xml
唯一命名的文件(按时间戳)
在应用程序开发阶段或短寿命应用程序(例如批量应用程序)中,每次启动新的应用程序时都希望创建一个新的日志文件。使用 <timestamp> 元素可以很容易地实现这一点。以下是一个示例。
示例:按时间戳唯一命名的 FileAppender 配置(logback-examples / src / main / resources / chapters / appenders / conf / logback-timestamp.xml)
传统格式
<configuration>
<!-- 在日志记录器上下文中插入以“yyyyMMdd'T'HHmmss”格式化的当前时间,键为“bySecond”。此值将对所有后续配置元素可用。 -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- 使用先前创建的时间戳创建一个唯一命名的日志文件 -->
<file>log-${bySecond}.txt</file>
<encoder>
<pattern>%logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
规范(1.3 版)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.FileAppender"/>
<timestamp name="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="FileAppender">
<file>log-${bySecond}.txt</file>
<encoder class="PatternLayoutEncoder">
<pattern>%logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
timestamp 元素有两个必需的属性 key 和 datePattern,以及一个可选的 timeReference 属性。key 属性是将时间戳作为变量在后续配置元素中可用的键的名称 变量。datePattern 属性表示将当前时间(解析配置文件时的时间)转换为字符串所使用的日期模式。日期模式应遵循 SimpleDateFormat 中定义的约定。timeReference 属性表示时间戳的时间参考。默认值是配置文件的解释/解析时间,即当前时间。但是,在某些情况下,使用上下文创建时间作为时间参考可能很有用。这可以通过将 timeReference 属性设置为 "contextBirth" 来实现。
通过运行以下命令,可以尝试使用 <timestamp> 元素:
java chapters.appenders.ConfigurationTester src/main/resources/chapters/appenders/conf/logback-timestamp.xml
要将日志记录器上下文的出生日期作为时间参考,您可以将 timeReference 属性设置为 "contextBirth",如下所示。
示例:使用上下文出生日期作为时间参考的时间戳(logback-examples/src/main/resources/chapters/appenders/conf/logback-timestamp-contextBirth.xml)
<configuration>
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"
timeReference="contextBirth"/>
...
</configuration>
RollingFileAppender
RollingFileAppender 扩展了 FileAppender,具有滚动日志文件的能力。例如,RollingFileAppender 可以将日志记录到名为 log.txt 的文件,并且一旦满足某种条件,就会将其日志记录目标更改为另一个文件。
有两个重要的子组件与 RollingFileAppender 交互。第一个 RollingFileAppender 子组件,即 RollingPolicy(见下文),负责执行滚动所需的操作。RollingFileAppender 的第二个子组件,即 TriggeringPolicy(见下文),将确定何时以及确切地何时发生滚动。因此,RollingPolicy 负责 什么,而 TriggeringPolicy 负责 何时。
要有任何用处,RollingFileAppender 必须同时设置 RollingPolicy 和 TriggeringPolicy。但是,如果其 RollingPolicy 还实现了 TriggeringPolicy 接口,则只需要显式指定前者。
以下是 RollingFileAppender 的可用属性:
| 属性名称 | 类型 | 描述 |
|---|---|---|
file | String | 参见 FileAppender 属性。请注意,如果文件为 null,则输出将仅写入由 RollingPolicy 指定的目标。 |
append | boolean | 参见 FileAppender 属性。 |
encoder | Encoder | 参见 OutputStreamAppender 属性。 |
rollingPolicy | RollingPolicy | 此选项是滚动发生时将指示 RollingFileAppender 行为的组件。请参阅下文了解更多信息。 |
triggeringPolicy | TriggeringPolicy | 此选项是将告知 RollingFileAppender 何时激活滚动程序的组件。请参阅下文了解更多信息。 |
prudent | boolean | 在谨慎模式中不支持 FixedWindowRollingPolicy。RollingFileAppender 支持谨慎模式,与 TimeBasedRollingPolicy 结合使用时有两个限制。1. 在谨慎模式下,不支持或允许文件压缩。(当一个 JVM 正在写入文件时,另一个 JVM 无法对其进行压缩。) 2. FileAppender 的文件属性不能被设置,必须保留为空白。事实上,大多数操作系统不允许在另一个进程打开文件时对文件进行重命名。另请参阅 FileAppender 的属性。 |
滚动策略概述
RollingPolicy 负责涉及文件移动和重命名的滚动程序。
RollingPolicy 接口如下所示:
package ch.qos.logback.core.rolling;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.spi.LifeCycle;
public interface RollingPolicy extends LifeCycle {
public void rollover() throws RolloverFailure;
public String getActiveFileName();
public CompressionMode getCompressionMode();
public void setParent(FileAppender appender);
}
rollover 方法完成了存档当前日志文件的工作。调用 getActiveFileName() 方法来计算当前日志文件的文件名(写入活动日志的位置)。根据 getCompressionMode 方法的指示,RollingPolicy 还负责确定压缩模式。最后,通过 setParent 方法,RollingPolicy 获得对其父级的引用。
基于时间的滚动策略
TimeBasedRollingPolicy 可能是最受欢迎的滚动策略。它基于时间定义了滚动策略,例如按天或按月。TimeBasedRollingPolicy 承担了触发滚动以及滚动的责任。事实上,TimeBasedTriggeringPolicy 实现了 RollingPolicy 和 TriggeringPolicy 接口。
TimeBasedRollingPolicy 的配置包括一个强制的 fileNamePattern 属性和几个可选属性。
| 属性名称 | 类型 | 描述 |
|---|---|---|
fileNamePattern | String | 强制的 fileNamePattern 属性定义了滚动(归档)日志文件的名称。其值应该包括文件的名称,加上适当放置的 %d 转换说明符。%d 转换说明符可以包含由 java.text.SimpleDateFormat 类指定的日期和时间模式。如果省略日期和时间模式,则假定默认模式为 yyyy-MM-dd。滚动周期由 fileNamePattern 的值推断而来。请注意, RollingFileAppender(TimeBasedRollingPolicy 的父级)中的文件属性可以设置为某个值,也可以省略(=null)。通过设置包含 FileAppender 的文件属性,您可以使活动日志文件的位置与归档的日志文件位置分离。当前日志始终将定位到由文件属性指定的文件中。因此,当前活动日志文件的名称随时间不变。但是,如果选择省略文件属性,则将针对每个周期基于 fileNamePattern 的值重新计算活动文件。在此配置中,除非指定文件压缩,否则不会发生滚动。下面的示例将阐明这一点。大括号中的日期和时间模式遵循 java.text.SimpleDateFormat 的约定。fileNamePattern 属性或日期和时间模式中的正斜杠 '/' 或反斜杠 '\' 将被解释为目录分隔符。多个 %d 说明符可以指定多个 %d 说明符,但其中只能有一个是主要的,即用于推断滚动周期的。所有其他标记都必须通过传递 'aux' 参数标记为辅助(请参见下面的示例)。多个 %d 说明符允许您以不同于滚动周期的文件夹结构组织归档文件。例如,下面显示的文件名模式将按年份和月份组织日志文件夹,但每天午夜都会滚动日志文件。/var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log时区 在某些情况下,您可能希望根据与主机不同的时区时钟滚动日志文件。可以在 %d 转换说明符中的日期和时间模式之后传递时区参数。例如:aFolder/test.%d{yyyy-MM-dd-HH, UTC}.log如果指定的时区标识符未知或拼写错误,则按照 TimeZone.getTimeZone(String)方法规范,假定为 GMT 时区。 |
maxHistory | int | 可选的 maxHistory 属性控制要保留的归档文件的最大数量,异步删除旧文件。例如,如果指定了每月滚动,并将 maxHistory 设置为 6,则将保留 6 个月的归档文件,超过 6 个月的文件将被删除。请注意,在移除旧的归档日志文件时,将适当地删除为了日志文件归档而创建的任何文件夹。将 maxHistory 设置为零将禁用归档移除。默认情况下,maxHistory 设置为零,即默认情况下不进行归档移除。 |
totalSizeCap | int | 可选的 totalSizeCap 属性控制所有归档文件的总大小。当总大小达到时,最老的归档文件将异步删除。totalSizeCap 属性还要求设置 maxHistory 属性。此外,总大小限制始终首先应用“最大历史”限制,然后应用“总大小限制”。totalSizeCap 属性可以通过在数字值后添加 KB、MB 或 GB 来指定为字节、千字节、兆字节或千兆字节单位。例如,5000000、5000KB、5MB 和 2GB 都是有效值,前三个等效。不带后缀的数字值被视为字节单位。默认情况下, totalSizeCap 设置为零,表示没有总大小限制。 |
cleanHistoryOnStart | boolean | 如果设置为 true,将在启动 appender 时执行归档移除。默认情况下,此属性设置为 false。通常在滚动时执行归档移除。但是,某些应用程序可能不会存活足够长的时间以触发滚动。因此,对于这样的短寿命应用程序,归档移除可能永远不会有机会执行。将 cleanHistoryOnStart 设置为 true 时,将在 appender 启动时执行归档移除。 |
这里有一些 fileNamePattern 值及其效果的解释。
| fileNamePattern | 滚动计划 | 示例 |
|---|---|---|
/wombat/foo.%d | 每天(午夜)滚动。由于省略了 %d 令牌说明符的可选时间和日期模式,假定了默认模式为 yyyy-MM-dd,对应于每天滚动。 | 文件属性未设置:2006 年 11 月 23 日期间,日志输出将写入文件 /wombat/foo.2006-11-23。午夜时分及之后,日志输出将定向至 /wombat/foo.2006-11-24。 文件属性设置为 /wombat/foo.txt:2006 年 11 月 23 日期间,日志输出将写入文件 /wombat/foo.txt。午夜时分,foo.txt 将重命名为 /wombat/foo.2006-11-23。新的 /wombat/foo.txt 文件将被创建,并在 11 月 24 日期间,日志输出将定向至 foo.txt。 |
/wombat/%d{yyyy/MM}/foo.txt | 每个月初滚动。 | 文件属性未设置:2006 年 10 月期间,日志输出将写入 /wombat/2006/10/foo.txt。10 月 31 日午夜及之后,日志输出将定向至 /wombat/2006/11/foo.txt。 文件属性设置为 /wombat/foo.txt:活动日志文件始终为 /wombat/foo.txt。2006 年 10 月期间,日志输出将写入 /wombat/foo.txt。10 月 31 日午夜, /wombat/foo.txt 将重命名为 /wombat/2006/10/foo.txt。新的 /wombat/foo.txt 文件将被创建,并在 11 月期间,日志输出将定向至 foo.txt。11 月 30 日午夜, /wombat/foo.txt 将重命名为 /wombat/2006/11/foo.txt,以此类推。 |
/wombat/foo.%d{yyyy-ww}.log | 每周初滚动。请注意,一周的第一天取决于语言环境。 | 与前面的情况类似,只是滚动将在每个新周的开始发生。 |
/wombat/foo%d{yyyy-MM-dd\_HH}.log | 每小时初滚动。 | 与前面的情况类似,只是滚动将在每个整点小时开始发生。 |
/wombat/foo%d{yyyy-MM-dd\_HH-mm}.log | 每分钟初滚动。 | 与前面的情况类似,只是滚动将在每分钟开始发生。 |
/wombat/foo%d{yyyy-MM-dd\_HH-mm, UTC}.log | 每分钟初滚动。 | 与前面的情况类似,只是文件名将以 UTC 表示。 |
/foo/%d{yyyy-MM,aux}/%d.log | 每天滚动。归档文件位于包含年份和月份的文件夹中。 | 在此示例中,第一个 %d 标记标记为 auxiliary。第二个 %d 标记,省略了时间和日期模式,因此假定为主要。因此,每天将发生滚动(%d 的默认值),并且文件夹名称将依赖于年份和月份。例如,在 2006 年 11 月期间,存档文件将全部放置在 /foo/2006-11/ 文件夹下,例如 /foo/2006-11/2006-11-14.log。 |
任何正斜杠或反斜杠字符都被解释为文件夹(目录)分隔符。根据需要将创建所需的任何文件夹。因此,您可以很容易地将日志文件放在单独的文件夹中。
自动文件压缩
TimeBasedRollingPolicy 支持自动文件压缩。如果 fileNamePattern 选项的值以 .gz 或 .zip 结尾,则启用此功能。
| fileNamePattern | 滚动计划 | 示例 |
|---|---|---|
/wombat/foo.%d.gz | 每天(午夜)滚动,并自动对归档文件进行 GZIP 压缩。 | 文件属性未设置:2009 年 11 月 23 日期间,日志输出将写入文件 /wombat/foo.2009-11-23。然而,午夜时分,该文件将被压缩为 /wombat/foo.2009-11-23.gz。11 月 24 日,日志输出将定向至 /wombat/folder/foo.2009-11-24,直至在第二天开始时滚动。 文件属性设置为 /wombat/foo.txt :2009 年 11 月 23 日期间,日志输出将写入文件 /wombat/foo.txt。午夜时分,该文件将被压缩并重命名为 /wombat/foo.2009-11-23.gz。将创建一个新的 /wombat/foo.txt 文件,用于 11 月 24 日剩余的日志输出。11 月 24 日午夜, /wombat/foo.txt 将被压缩并重命名为 /wombat/foo.2009-11-24.gz,以此类推。 |
fileNamePattern 具有双重目的。首先,通过研究模式,logback 计算所请求的滚动周期。其次,它计算每个归档文件的名称。请注意,两种不同的模式可以指定相同的周期性。模式 yyyy-MM 和 yyyy@MM 都指定了每月滚动,尽管生成的归档文件将带有不同的名称。
通过设置文件属性,您可以将活动日志文件的位置与归档日志文件的位置解耦。日志输出将定向到由文件属性指定的文件中。因此,活动日志文件的名称随时间不会改变。然而,如果选择省略文件属性,那么每个周期的活动文件将根据 fileNamePattern 的值重新计算。通过保持文件选项未设置,您可以避免在发生滚动时存在外部文件句柄引用日志文件导致的文件重命名错误。
maxHistory 属性控制要保留的归档文件的最大数量,并删除较旧的文件。例如,如果您指定按月滚动,并将 maxHistory 设置为 6,则将保留 6 个月的存档文件,超过 6 个月的文件将被删除。请注意,随着旧的归档日志文件被删除,为了日志文件归档而创建的任何文件夹将适当地被删除。
出于各种技术原因,滚动不是由时钟驱动的,而是取决于日志事件的到达。例如,在 2002 年 3 月 8 日,假设 fileNamePattern 设置为 yyyy-MM-dd(每日滚动),午夜后第一个事件的到达将触发滚动。如果在午夜后的 23 分钟 47 秒内没有日志事件,则实际上滚动将发生在 3 月 9 日上午 00:23:47,而不是凌晨 0:00。因此,根据事件的到达速率,可能会出现一些延迟触发滚动。然而,无论延迟如何,滚动算法都被认为是正确的,就是说,在某个特定期间生成的所有日志事件将被输出到正确的文件中,限定了该期间。
以下是 RollingFileAppender 与 TimeBasedRollingPolicy 结合使用的示例配置。
示例:使用 TimeBasedRollingPolicy 配置 RollingFileAppender 的示例配置(logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingTimeBased.xml)
传统格式
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每日滚动 -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 保留30天的历史记录,总大小限制为3GB -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
规范格式 (1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
<appender name="FILE" class="RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
下一个配置示例演示了在谨慎模式下使用 RollingFileAppender 与 TimeBasedRollingPolicy 的示例。
示例:使用 TimeBasedRollingPolicy 配置 RollingFileAppender 的谨慎模式示例配置(logback-examples/src/main/resources/chapters/appenders/conf/logback-PrudentTimeBasedRolling.xml)
传统格式
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 支持多 JVM 写入同一日志文件 -->
<prudent>true</prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
规范格式 (1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>
<appender name="FILE" class="RollingFileAppender">
<prudent>true</prudent>
<rollingPolicy class="TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
基于大小和时间的滚动策略
有时候你可能希望按日期归档文件,同时限制每个日志文件的大小,特别是如果后期处理工具对日志文件施加了大小限制。为了满足这个需求,logback 提供了 SizeAndTimeBasedRollingPolicy。
请注意,TimeBasedRollingPolicy 已经允许限制归档日志文件的总大小。如果你只希望限制日志归档文件的总大小,那么上面描述的 TimeBasedRollingPolicy 并设置 totalSizeCap 属性就足够了。此外,由于文件重命名是一个相对较慢且容易出问题的过程,我们不建议使用 SizeAndTimeBasedRollingPolicy,除非你有一个真实的用例。
基于大小的滚动依赖于 "%i" 转换标记以及 "%d" 。%i 和 %d 标记都是必需的。每当当前日志文件在当前时间段结束之前达到 maxFileSize 时,它将被归档,并以递增的索引从 0 开始。
下表列出了适用于 SizeAndTimeBasedRollingPolicy 的属性。请注意,这些属性是与 TimeBasedRollingPolicy 的属性相补充的。
| 属性名 | 类型 | 描述 |
|---|---|---|
maxFileSize | FileSize | 每当当前日志文件在当前时间段结束之前达到 maxFileSize 时,它将被归档,并以递增的索引从 0 开始。可以使用 "FileSize" 单位指定以字节、千字节、兆字节或千兆字节为单位的选项,通过在数字值后加上 KB、MB 或 GB 来指定。例如,5000000、5000KB、5MB 和 2GB 都是有效的值,前三个等价。 |
checkIncrement | Duration | ==自 1.3.12/1.4.12 起可用== 由于检查文件大小是一个相对耗时的操作,默认情况下每隔 60 秒执行一次。然而,你可以设置不同的检查增量作为 duration。 |
下面是一个演示基于时间和大小的日志文件归档的示例配置文件。
示例: SizeAndTimeBasedFNATP 的配置文件 (logback-examples/src/main/resources/chapters/appenders/conf/logback-sizeAndTime.xml)
传统格式
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 每天滚动 -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- 每个文件最多为 100MB,保留 60 天的历史记录,但总大小不超过 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>
规范格式 (1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"/>
<appender name="ROLLING" class="RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="SizeAndTimeBasedRollingPolicy">
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING"/>
</root>
</configuration>
注意 "%i" 转换标记以及 "%d"。 %i 和 %d 标记都是必需的。 每当当前日志文件在当前时间段结束之前达到 maxFileSize 时,它将被归档,并以递增的索引从 0 开始。
基于大小和时间的归档支持删除旧的归档文件。你需要用 maxHistory 属性指定要保留的时间段数。当你的应用停止并重新启动时,日志记录将继续在正确的位置进行,即在当前时间段的最大索引号处。
在 1.1.7 版本之前,本文档提到了一个名为 SizeAndTimeBasedFNATP 的组件。然而,由于 SizeAndTimeBasedRollingPolicy 提供了更简单的配置结构,我们不再记录 SizeAndTimeBasedFNATP。尽管如此,以前使用 SizeAndTimeBasedFNATP 的配置文件仍然可以正常工作。实际上,SizeAndTimeBasedRollingPolicy 是通过 SizeAndTimeBasedFNATP 子组件实现的。
FixedWindowRollingPolicy
鉴于文件重命名是一个相对缓慢的过程,并且充满问题,我们认为 FixedWindowRollingPolicy 是一种不推荐使用的策略。
在执行滚动时,FixedWindowRollingPolicy 根据下面描述的固定窗口算法来对文件进行重命名。
fileNamePattern 选项表示归档(滚动)日志文件的文件名模式。此选项是必需的,且模式中必须包含整数标记 %i 。
以下是 FixedWindowRollingPolicy 的可用属性:
| 属性名称 | 类型 | 描述 |
|---|---|---|
minIndex | int | 此选项表示窗口索引的下界。 |
maxIndex | int | 此选项表示窗口索引的上界。 |
fileNamePattern | String | 此选项表示在重命名日志文件时 FixedWindowRollingPolicy 将遵循的模式。它必须包含字符串 %i ,该字符串将指示当前窗口索引值的位置。例如,使用 MyLogFile%i.log 关联最小和最大值分别为 1 和 3 将产生归档文件名为 MyLogFile1.log、MyLogFile2.log 和 MyLogFile3.log。 请注意,文件压缩也是通过此属性指定的。例如,将 fileNamePattern 设置为 MyLogFile%i.log.zip 意味着归档文件必须使用 zip 格式压缩;也支持 gz 格式。 |
鉴于固定窗口滚动策略需要与窗口大小一样多的文件重命名操作,强烈不建议使用大窗口大小。当用户指定大值时,当前实现将自动将窗口大小减小到 20。
让我们来看一个更具体的固定窗口滚动策略的例子。假设将 minIndex 设置为 1,maxIndex 设置为 3,fileNamePattern 属性设置为 foo%i.log,文件属性设置为 foo.log。
| 滚动次数 | 活动输出目标 | 归档日志文件 | 描述 |
|---|---|---|---|
| 0 | foo.log | - | 尚未发生滚动,logback 记录在初始文件中。 |
| 1 | foo.log | foo1.log | 第一次滚动。foo.log 被重命名为 foo1.log。创建一个新的 foo.log 文件并成为活动输出目标。 |
| 2 | foo.log | foo1.log, foo2.log | 第二次滚动。foo1.log 被重命名为 foo2.log。foo.log 被重命名为 foo1.log。创建一个新的 foo.log 文件并成为活动输出目标。 |
| 3 | foo.log | foo1.log, foo2.log, foo3.log | 第三次滚动。foo2.log 被重命名为 foo3.log。foo1.log 被重命名为 foo2.log。foo.log 被重命名为 foo1.log。创建一个新的 foo.log 文件并成为活动输出目标。 |
| 4 | foo.log | foo1.log, foo2.log, foo3.log | 在这次和随后的滚动中,滚动从删除 foo3.log 开始。其他文件通过递增它们的索引重命名,如前面步骤所示。在这次和随后的滚动中,将有三个归档日志文件和一个活动日志文件。 |
下面的配置文件提供了一个配置 RollingFileAppender 和 FixedWindowRollingPolicy 的示例。请注意,即使 File 选项包含与 fileNamePattern 选项传达的相同信息,但 File 选项也是必需的。
示例:使用 FixedWindowRollingPolicy 配置 RollingFileAppender 的样本配置(logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingFixedWindow.xml)
传统格式
<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} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
规范格式 (1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"/>
<appender name="FILE" class="RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="FixedWindowRollingPolicy">
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
触发策略概述
TriggeringPolicy 的实现负责指示 RollingFileAppender 何时执行滚动操作。
TriggeringPolicy 接口只包含一个方法。
package ch.qos.logback.core.rolling;
import java.io.File;
import ch.qos.logback.core.spi.LifeCycle;
public interface TriggeringPolicy<E> extends LifeCycle {
public boolean isTriggeringEvent(final File activeFile, final <E> event);
}
isTriggeringEvent() 方法以当前活动文件和正在处理的日志事件作为参数。具体的实现根据这些参数确定是否应该进行滚动操作。
最常用的触发策略是 TimeBasedRollingPolicy,它也充当滚动策略,并且在前面已经讨论过其他滚动策略。
SizeBasedTriggeringPolicy
SizeBasedTriggeringPolicy 根据当前活动文件的大小来触发滚动。如果文件大小超过指定的大小,它将通知拥有的 RollingFileAppender 触发现有活动文件的滚动。
SizeBasedTriggeringPolicy 只接受一个参数 maxFileSize,默认值为 10MB。
maxFileSize 参数可以以字节、千字节、兆字节或千兆字节的形式指定,通过在数字值后面加上 KB、MB 和 GB 后缀。例如,5000000、5000KB、5MB 和 2GB 都是有效的值,其中前三个是等效的。
下面是一个示例配置,使用 SizeBasedTriggeringPolicy 在日志文件达到 5MB 大小时触发滚动的 RollingFileAppender。
示例:使用 SizeBasedTriggeringPolicy 的 RollingFileAppender 的配置示例 (logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingSizeBased.xml)
传统格式
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>test.%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} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
规范格式 (1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
<import class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"/>
<import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
<import class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"/>
<appender name="FILE" class="RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="FixedWindowRollingPolicy">
<fileNamePattern>test.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder class="PatternLayoutEncoder">
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</configuration>
版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:www.liujiajia.me/2023/12/6/l…