Logback 手册 - 第四章:Appenders
来源:https://logback.qos.ch/manual/appenders.html
作者: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
WARNING
为了运行本章中的示例,您需要确保某些 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>
<?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>
<?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>
<?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>
<?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>
<?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>
<?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>
Logback Classic
在 logback-core 中,日志事件是通用的,但在 logback-classic 中,它们始终是 ILoggingEvent
的实例。Logback-classic 实际上只是一个专门处理 ILoggingEvent
实例的处理管道。
SocketAppender 和 SSLSocketAppender
到目前为止介绍的 appender 只能记录到本地资源。相比之下,SocketAppender
被设计用于通过在网络上传输序列化的 ILoggingEvent
实例来记录到远程实体。使用 SocketAppender
时,在网络上传输的日志事件是明文发送的。然而,当使用 SSLSocketAppender
时,日志事件将通过安全通道传送。
序列化事件的实际类型是 LoggingEventVO
,它实现了 ILoggingEvent
接口。然而,远程记录对于日志事件来说是非侵入式的。在反序列化后,事件可以被记录,就好像它是在本地生成的一样。在不同机器上运行的多个 SocketAppender
实例可以将它们的日志输出定向到一个格式固定的中央日志服务器。SocketAppender
不需要关联布局,因为它会将序列化的事件发送到一个远程服务器。SocketAppender
在 传输控制协议(TCP) 层之上运行,提供可靠、有序、受控的端到端八位字节流。因此,如果远程服务器是可访问的,日志事件最终会到达那里。否则,如果远程服务器宕机或不可访问,日志事件将会被丢弃。如果服务器重新上线,事件传输将会自动恢复。这种透明的重新连接是由一个连接器线程执行的,它会定期尝试连接服务器。
日志事件会被本机 TCP 实现自动缓冲。这意味着,如果客户端与服务器的连接速度比事件产生速度要慢但仍然比较快,客户端不会受到网络连接缓慢的影响。然而,如果网络连接速度比事件产生速度更慢,那么客户端只能以网络速度进行进展。特别是,在网络链路到服务器完全断开的极端情况下,客户端最终会被阻塞。另外,如果网络链路正常,但服务器宕机,客户端不会被阻塞,尽管由于服务器不可用,日志事件将丢失。
即使 SocketAppender
不再附加到任何 logger,只要存在连接器线程,它就不会被垃圾回收。只有当与服务器的连接断开时才存在连接器线程。为了避免这个垃圾回收问题,你应该显式关闭 SocketAppender
。长时间运行的应用程序,如果创建/销毁许多 SocketAppender
实例,应该注意这个垃圾回收问题。大多数其他应用程序可以安全地忽略它。如果托管 SocketAppender
的 JVM 在 SocketAppender
关闭之前退出,无论是显式关闭还是在垃圾回收后,可能会有未传输的数据。这在基于 Windows 的系统中很常见。为了避免数据丢失,通常在退出应用程序之前调用 close()
方法或 LoggerContext
的 stop()
方法来关闭 SocketAppender
。
远程服务器由 remoteHost
和 port
属性标识。SocketAppender
的属性列在下表中。SSLSocketAppender
支持许多额外的配置属性,详细信息请参见 Using SSL 部分。
属性名称 | 类型 | 描述 |
---|---|---|
includeCallerData | boolean | includeCallerData 选项接受布尔值。如果为 true ,则调用者数据将可用于远程主机。默认情况下,不会将调用者数据发送到服务器。 |
port | int | 远程服务器的端口号。 |
reconnectionDelay | Duration | reconnectionDelay 选项接受持续时间字符串,如“10 seconds”,表示在每次连接到服务器失败时等待的时间。该选项的默认值为 30 秒。将此选项设置为零会关闭重新连接功能。请注意,如果成功连接到服务器,将不会有连接器线程存在。 |
queueSize | int | queueSize 属性接受一个整数(大于零),表示保留用于传递到远程接收器的日志事件数量。当队列大小为 1 时,事件传送到远程接收器是同步的。当队列大小大于 1 时,新事件将被排队,假设队列中有空间可用。使用大于 1 的队列长度可以通过消除瞬时网络延迟引起的延迟来提高性能。另请参阅 eventDelayLimit 属性。 |
eventDelayLimit | Duration | eventDelayLimit 选项接受持续时间字符串,如“10 seconds”。它表示在本地队列已满时等待丢弃事件之前的时间,即已包含 queueSize 个事件。如果远程主机持续缓慢接受事件,可能会发生这种情况。该选项的默认值为 100 毫秒。 |
remoteHost | String | 服务器的主机名。 |
ssl | SSLConfiguration | 仅对 SSLSocketAppender 受支持,该属性提供将被 appender 使用的 SSL 配置,详情请参见 Using SSL。 |
日志服务器选项
标准的 Logback Classic 分发版本包括两个可用于接收 SocketAppender
或 SSLSocketAppender
日志事件的服务器选项。
ServerSocketReceiver
及其启用 SSL 的对应组件SSLServerSocketReceiver
,可以在应用程序的 logback.xml 配置文件中配置,以接收来自远程 socket appender 的事件。请参阅 Receivers 获取配置详细信息和使用示例。SimpleSocketServer
及其启用 SSL 的对应组件SimpleSSLSocketServer
均提供一个易于使用的独立 Java 应用程序,可从您的 shell 命令行界面进行配置和运行。这些应用程序简单地等待来自SocketAppender
或SSLSocketAppender
客户端的日志事件。每个接收到的事件都根据本地服务器策略进行记录。下面给出了使用示例。
使用 SimpleSocketServer
SimpleSocketServer
应用程序接受两个命令行参数:port 和 configFile;其中 port 是要监听的端口号,configFile 是以 XML 格式的配置脚本。
假设您在 logback-examples/ 目录中,使用以下命令启动 SimpleSocketServer
:
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
src/main/java/chapters/appenders/socket/server1.xml
其中 6000 是要监听的端口号,server1.xml 是一个将 ConsoleAppender
和 RollingFileAppender
添加到根 logger 的配置脚本。启动 SimpleSocketServer
后,您可以使用 SocketAppender
从多个客户端向其发送日志事件。本手册附带的示例包括两个这样的客户端:chapters.appenders.SocketClient1
和 chapters.appenders.SocketClient2
。这两个客户端等待用户在控制台上输入一行文本。文本被封装为 debug 级别的日志事件,然后发送到远程服务器。这两个客户端在 SocketAppender
的配置上有所不同。SocketClient1
以编程方式配置 appender,而 SocketClient2
需要一个配置文件。
假设 SimpleSocketServer
正在本地主机上运行,您可以使用以下命令连接到它:
java chapters.appenders.socket.SocketClient1 localhost 6000
每行你输入的内容都应该出现在上一步中启动的 SimpleSocketServer
的控制台上。如果停止并重新启动 SimpleSocketServer
,客户端将会透明地重新连接到新的服务器实例,尽管在断开连接时生成的事件将会被简单地(并且是不可撤销地)丢失。
与 SocketClient1
不同,示例应用程序 SocketClient2
本身不配置 logback。它需要一个 XML 格式的配置文件。下面显示的配置文件 client1.xml 创建了一个 SocketAppender
并将其附加到根记录器。
示例:SocketAppender 配置(logback-examples/src/main/resources/chapters/appenders/socket/client1.xml)
传统方式
<configuration>
<appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender">
<remoteHost>${host}</remoteHost>
<port>${port}</port>
<reconnectionDelay>10000</reconnectionDelay>
<includeCallerData>${includeCallerData}</includeCallerData>
</appender>
<root level="DEBUG">
<appender-ref ref="SOCKET" />
</root>
</configuration>
规范方式(1.3)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.net.SocketAppender"/>
<appender name="SOCKET" class="SocketAppender">
<remoteHost>${host}</remoteHost>
<port>${port}</port>
<reconnectionDelay>10000</reconnectionDelay>
<includeCallerData>${includeCallerData}</includeCallerData>
</appender>
<root level="DEBUG">
<appender-ref ref="SOCKET"/>
</root>
</configuration>
请注意,在上述配置脚本中,remoteHost
、port
和 includeCallerData
属性的值不是直接给出的,而是作为替换变量键。可以将这些变量的值指定为系统属性:
java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \
chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml
这个命令应该产生类似于之前 SocketClient1
示例的结果。
请允许我们重申一遍,日志事件的序列化并不具有侵入性。反序列化的事件携带与任何其他日志事件相同的信息。可以像对待本地生成的事件一样操作它;只是默认情况下序列化的日志事件不包括调用者数据。以下是一个例子来说明这一点。首先,使用以下命令启动 SimpleSocketServer
:
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
src/main/java/chapters/appenders/socket/server2.xml
配置文件 server2.xml 创建了一个 ConsoleAppender
,其布局输出调用者的文件名和行号以及其他信息。如果像以前那样使用配置文件 client1.xml 运行 SocketClient2
,你会注意到服务器端的输出将包含两个问号在括号之间,而不是调用者的文件名和行号:
2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi
通过将 includeCallerData
选项设置为 true
,可以简单地改变结果。使用以下命令会起到作用:
java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \
chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml
由于反序列化的事件可以像本地生成的事件一样处理,甚至可以将它们发送到第二个服务器进行进一步处理。作为练习,您可能希望设置两个服务器,其中第一个服务器将其从客户端接收到的事件隧道传输到第二个服务器。
使用 SimpleSSLSocketServer
SimpleSSLSocketServer
需要与 SimpleSocketServer
相同的 port 和 configFile 命令行参数。此外,您还必须在命令行上提供日志服务器的 X.509 凭据的位置和密码,使用系统属性指定。
假设您位于 logback-examples/ 目录下,请使用以下命令启动 SimpleSSLSocketServer
:
java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks -Djavax.net.ssl.keyStorePassword=changeit ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 src/main/java/chapters/appenders/socket/ssl/server.xml
此示例使用适用于测试和实验的 X.509 凭据运行 SimpleSSLSocketServer
。在生产环境中使用 SimpleSSLSocketServer
之前,您应获取适当的 X.509 凭据来标识您的日志服务器。有关更多详细信息,请参阅 使用 SSL。
由于服务器配置在根元素上指定了 debug="true"
,因此您将在服务器的启动日志中看到将要使用的 SSL 配置。这对于验证本地安全策略是否正确实施非常有用。
在运行 SimpleSSLSocketServer
时,您可以使用 SSLSocketAppender
连接到服务器。以下示例显示了所需的 appender 配置:
示例:SSLSocketAppender 配置(logback-examples/src/main/resources/chapters/appenders/socket/ssl/client.xml)
<configuration debug="true">
<appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender">
<remoteHost>${host}</remoteHost>
<port>${port}</port>
<reconnectionDelay>10000</reconnectionDelay>
<ssl>
<trustStore>
<location>${truststore}</location>
<password>${password}</password>
</trustStore>
</ssl>
</appender>
<root level="DEBUG">
<appender-ref ref="SOCKET" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration debug="true">
<import class="ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBean"/>
<import class="ch.qos.logback.classic.net.SSLSocketAppender"/>
<appender name="SOCKET" class="SSLSocketAppender">
<remoteHost>${host}</remoteHost>
<port>${port}</port>
<reconnectionDelay>10000</reconnectionDelay>
<ssl>
<trustStore class="KeyManagerFactoryFactoryBean">
<location>${truststore}</location>
<password>${password}</password>
</trustStore>
</ssl>
</appender>
<root level="DEBUG">
<appender-ref ref="SOCKET"/>
</root>
</configuration>
请注意,与先前的示例一样,remoteHost
、port
的值使用替代变量键指定。另外,请注意 ssl
属性及其嵌套的 truststore
属性的存在,它使用替代变量指定了信任库的位置和密码。由于我们的示例服务器使用自签名证书,因此需要此配置。有关 SSLSocketAppender
的 SSL 配置属性的更多信息,请参阅 使用 SSL。
我们可以通过将替代变量值作为系统属性在命令行上指定,使用此配置运行客户端应用程序:
java -Dhost=localhost -Dport=6000 -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks -Dpassword=changeit chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/ssl/client.xml
与先前的示例一样,当客户端应用程序提示您输入消息时,您可以键入消息,该消息将通过安全通道传递到日志服务器,并在控制台上显示出来。
请注意,命令行中给出的 truststore 属性指定了一个文件 URL,用于标识信任库的位置。您还可以使用类路径 URL,如 使用 SSL 中所述。
正如我们之前在服务器启动时看到的那样,因为客户端配置在根元素上指定了 debug="true"
,客户端的启动日志包括 SSL 配置的详细信息,以帮助审计本地策略的符合性。
ServerSocketAppender 和 SSLServerSocketAppender
之前讨论的 SocketAppender
组件(以及它的支持 SSL 的对应组件)旨在允许应用程序通过网络连接到远程日志服务器,以便将日志事件传递给服务器。在某些情况下,应用程序发起与远程日志服务器的连接可能不方便或不可行。针对这些情况,Logback 提供了 ServerSocketAppender
。
ServerSocketAppender
不是主动连接到远程日志服务器,而是被动地监听一个 TCP 套接字,等待来自远程客户端的连接。传递给 appender 的日志事件将分发给每个已连接的客户端。当没有客户端连接时,产生的日志事件将 被直接丢弃。
除了基本的 ServerSocketAppender
,Logback 还提供了 SSLServerSocketAppender
,它使用安全的加密通道将日志事件分发给每个已连接的客户端。此外,支持 SSL 的 appender 完全支持基于互相认证的证书,可以确保只有授权的客户端才能连接到 appender 接收日志事件。
用于在网络上传输日志事件的编码方法与 SocketAppender
使用的方法相同;每个事件都是 ILoggingEvent
的序列化实例。唯一不同的是连接初始化的方向。SocketAppender
作为主动方建立与日志服务器的连接,而 ServerSocketAppender
是被动方,它监听来自客户端的连接。
ServerSocketAppender
子类型仅用于与 Logback 的 接收器 组件配合使用。有关此组件类型的附加信息,请参阅 Receivers。
ServerSocketAppender
支持以下配置属性:
属性名 | 类型 | 描述 |
---|---|---|
address | String | appender 监听的本地网络接口地址。如果未指定此属性,appender 将监听所有网络接口。 |
includeCallerData | boolean | 如果为 true ,则调用者数据将可用于远程主机。默认情况下,不会将任何调用者数据发送到客户端。 |
port | int | appender 监听的端口号。 |
ssl | SSLConfiguration | 仅对 SSLServerSocketAppender 支持,此属性提供 appender 使用的 SSL 配置,详细描述在 Using SSL 中。 |
以下示例演示了使用 ServerSocketAppender
的配置:
示例:基本 ServerSocketAppender 配置 (logback-examples/src/main/resources/chapters/appenders/socket/server4.xml)
<configuration debug="true">
<appender name="SERVER"
class="ch.qos.logback.classic.net.server.ServerSocketAppender">
<port>${port}</port>
<includeCallerData>${includeCallerData}</includeCallerData>
</appender>
<root level="debug">
<appender-ref ref="SERVER" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration debug="true">
<import class="ch.qos.logback.classic.net.server.ServerSocketAppender"/>
<appender name="SERVER" class="ServerSocketAppender">
<port>${port}</port>
<includeCallerData>${includeCallerData}</includeCallerData>
</appender>
<root level="debug">
<appender-ref ref="SERVER"/>
</root>
</configuration>
请注意,此配置与仅使用 SocketAppender
的先前示例配置不同之处在于 appender 指定的 类 以及 remoteHost
属性的缺失——此 appender 被动地等待远程主机的传入连接,而不是打开与远程日志服务器的连接。
下面的示例演示了使用 SSLServerSocketAppender
的配置:
示例:基本 SSLServerSocketAppender 配置 (logback-examples/src/main/resources/chapters/appenders/socket/ssl/server3.xml)
<configuration debug="true">
<appender name="SERVER"
class="ch.qos.logback.classic.net.server.SSLServerSocketAppender">
<port>${port}</port>
<includeCallerData>${includeCallerData}</includeCallerData>
<ssl>
<keyStore>
<location>${keystore}</location>
<password>${password}</password>
</keyStore>
</ssl>
</appender>
<root level="debug">
<appender-ref ref="SERVER" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration debug="true">
<import class="ch.qos.logback.classic.net.server.SSLServerSocketAppender"/>
<import class="ch.qos.logback.core.net.ssl.KeyStoreFactoryBean"/>
<appender name="SERVER" class="SSLServerSocketAppender">
<port>${port}</port>
<includeCallerData>${includeCallerData}</includeCallerData>
<ssl>
<keyStore class="KeyStoreFactoryBean">
<location>${keystore}</location>
<password>${password}</password>
</keyStore>
</ssl>
</appender>
<root level="debug">
<appender-ref ref="SERVER"/>
</root>
</configuration>
此配置与先前配置的主要区别在于 appender 的 class 属性标识了 SSLServerSocketAppender
类型,并且嵌套的 ssl 元素存在,它在此示例中指定了包含 appender 的 X.509 凭证的密钥库的配置。有关 SSL 配置属性的信息,请参阅 Using SSL。
由于 ServerSocketAppender
子类型设计用于与接收器组件配合使用,我们将推迟在名为 Receivers 的章节中提供示例。
SMTPAppender
SMTPAppender
累积一个或多个固定大小的缓冲区中的日志事件,并在发生用户指定的事件后将相应缓冲区的内容以电子邮件的形式发送。SMTP 邮件传输(发送)是异步执行的。默认情况下,电子邮件传输由级别为 ERROR 的日志事件触发。此外,默认情况下,所有事件都使用单个缓冲区。
下表总结了 SMTPAppender
的各种属性。
属性名称 | 类型 | 描述 |
---|---|---|
smtpHost | String | SMTP 服务器的主机名。此参数为必填项。 |
smtpPort | int | SMTP 服务器侦听的端口。默认为 25。 |
to | String | 收件人的电子邮件地址作为“模式”。每次出站电子邮件的输入都是触发事件的结果。可以通过用逗号分隔目标地址来指定多个收件人。或者,也可以使用多个 <to> 元素来指定多个收件人。 |
from | String | SMTPAppender 发送的电子邮件消息的发起人,格式为标准的电子邮件地址格式。如果您希望包含发件人的名称,则使用格式 "AdamSmith<smith@moral.org>" ,使得消息看起来来自 "AdamSmith<smith@moral.org>" 。 |
subject | String | 电子邮件的主题。它可以是任何被 PatternLayout 接受为有效转换模式的值。布局将在下一章中讨论。发出的电子邮件消息将具有与触发电子邮件消息的日志事件上应用该模式相对应的主题行。 假设将主题选项设置为 "Log:%logger - %msg" ,并且触发事件的记录器命名为 "com.foo.Bar" ,并包含消息“Hello World”,则发出的电子邮件将具有主题行“Log:com.foo.Bar - Hello World”。默认情况下,此选项设置为 "%logger{20} - %m" 。 |
discriminator | Discriminator | 借助 Discriminator ,SMTPAppender 可以根据鉴别器返回的值将传入的事件分散到不同的缓冲区中。默认的鉴别器始终返回相同的值,以便所有事件都使用同一个缓冲区。通过指定除默认鉴别器之外的鉴别器,可以接收包含特定用户、用户会话或客户端 IP 地址的事件的电子邮件消息。 |
evaluator | IEvaluator | 通过创建新的 <EventEvaluator/> 元素来声明此选项。用户希望用作 SMTPAppender 的 Evaluator 的类的名称需要通过 class 属性来指定。在没有此选项的情况下, SMTPAppender 被分配一个实例 OnErrorEvaluator ,当它遇到等级为 ERROR 或更高的事件时触发电子邮件传输。Logback 附带了几个其他评估器,即 OnMarkerEvaluator (下面讨论)和一个强大的评估器称为 JaninoEventEvaluator ,在 另一章节 中进行了讨论。 |
cyclicBufferTracker | CyclicBufferTracker | 如名称所示,CyclicBufferTracker 类的一个实例跟踪循环缓冲区。它基于鉴别器返回的键进行跟踪(见上文)。如果不指定 cyclicBufferTracker ,则将自动创建 CyclicBufferTracker 的实例。默认情况下,此实例将在循环缓冲区中保留 256 个事件。您可以使用 bufferSize 选项更改大小。 |
username | String | 在纯文本用户/密码身份验证期间使用的用户名值。默认情况下,此参数为 null 。 |
password | String | 用于纯文本用户/密码身份验证的密码值。默认情况下,此参数为 null 。 |
STARTTLS | boolean | 如果将此参数设置为 true ,则此 appender 将发出 STARTTLS 命令(如果服务器支持),导致连接切换到 SSL。请注意,连接最初是非加密的。默认情况下,此参数设置为 false 。 |
SSL | boolean | 如果将此参数设置为 true ,则此 appender 将打开与服务器的 SSL 连接。默认情况下,此参数设置为 false 。 |
charsetEncoding | String | 发出的电子邮件消息将以指定的 字符集 进行编码。对于大多数情况,UTF-8 这种默认字符集编码效果良好。 |
localhost | String | 如果 SMTP 客户端的主机名未正确配置,例如,如果客户端主机名不是完全限定的,则某些 SMTP 服务器可能会拒绝由客户端发送的 HELO/EHLO 命令。为了解决此问题,您可以将 localhost 属性的值设置为客户端主机的完全限定名称。另请参见 com.sun.mail.smtp 包的文档中的 "mail.smtp.localhost" 属性。 |
asynchronousSending | boolean | 此属性确定是否异步执行电子邮件传输。默认情况下,asynchronousSending 属性为'true' 。但是,在某些情况下,异步发送可能不合适。例如,如果您的应用程序使用 SMTPAppender 在响应致命错误时发送警报,然后退出,相关线程可能没有时间发送警报电子邮件。在这种情况下,将 asynchronousSending 属性设置为 'false' 以进行同步电子邮件传输。 |
includeCallerData | boolean | 默认情况下,includeCallerData 设置为 false 。如果启用了 asynchronousSending ,并且希望在日志中包含调用者数据,则应将 includeCallerData 设置为 true 。 |
sessionViaJNDI | boolean | SMTPAppender 依赖于 javax.mail.Session 发送电子邮件消息。默认情况下,sessionViaJNDI 设置为 false ,因此 SMTPAppender 自己使用用户指定的属性构建 javax.mail.Session 实例。如果将 sessionViaJNDI 属性设置为 true ,则将通过 JNDI 检索 javax.mail.Session 对象。另请参见 jndiLocation 属性。通过 JNDI 检索会话可以减少需要在相同信息上配置/重新配置的位置数量,使您的应用程序更加干燥。有关在 Tomcat 中配置资源的更多信息,请参见 JNDI 资源操作指南。注意:如文档中所述,请务必从您的 Web 应用程序的 WEB-INF/lib 文件夹中删除 mail.jar 和 activation.jar,以便从 JNDI 中检索 Session 。 |
jndiLocation | String | jndiLocation 是 javax.mail.Session 在 JNDI 中放置的位置。默认情况下,jndiLocation 设置为 "java:comp/env/mail/Session" 。 |
SMTPAppender
在循环缓冲区中仅保留最后的 256 个日志事件,并在缓冲区已满时丢弃较旧的事件。因此,由 SMTPAppender
发送的任何电子邮件中传递的日志事件数量上限为 256。这样可以将内存需求限制在合理范围内,同时仍然提供了合理数量的应用程序上下文。
SMTPAppender
依赖于 JavaMail API。它已经与 JavaMail API 版本 1.4 进行了测试。JavaMail API 需要 JavaBeans Activation Framework 包。您可以从它们各自的网站上下载 JavaMail API 和 JavaBeans Activation Framework。在尝试以下示例之前,请确保将这两个 jar 文件放置在类路径中。
一个示例应用程序 chapters.appenders.mail.EMail
生成一系列日志消息,然后是一个错误消息。它有两个参数。第一个参数是生成的日志事件数量的整数。第二个参数是 logback 配置文件。由 EMail 应用程序生成的最后一个日志事件(ERROR)将触发电子邮件消息的传输。
这是一个用于 Email
应用程序的示例配置文件:
示例:一个 SMTPAppender
配置文件示例(logback-examples/src/main/resources/chapters/appenders/mail/mail1.xml)
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>YOUR-SMTP-HOST-ADDRESS</smtpHost>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to> <!-- 可以添加其他目标 -->
<from>SENDER-EMAIL</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level %logger{35} - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<import class="ch.qos.logback.classic.PatternLayout"/>
<appender name="EMAIL" class="SMTPAppender">
<smtpHost>YOUR-SMTP-HOST-ADDRESS</smtpHost>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to>
<from>SENDER-EMAIL</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="PatternLayout">
<pattern>%date %-5level %logger{35} - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL"/>
</root>
</configuration>
在使用上述配置文件尝试 chapters.appenders.mail.Email
应用程序之前,您必须将 smtpHost
、to
和 from
属性设置为适合您环境的值。在配置文件中设置正确的值后,执行以下命令:
java chapters.appenders.mail.EMail 100 src/main/java/chapters/appenders/mail/mail1.xml
您指定的收件人应该收到包含 100 个由 PatternLayout
格式化的日志事件的电子邮件消息。下图是 Mozilla Thunderbird 显示的结果电子邮件消息。
在下一个示例配置文件 mail2.xml 中,smtpHost
、to
和 from
属性的值由变量替换确定。这是 mail2.xml 的相关部分。
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
您可以通过命令行传递所需的参数:
java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
chapters.appenders.mail.EMail 10000 src/main/java/chapters/appenders/mail/mail2.xml
请确保用适合您环境的值替换上述参数。
请注意,在这个最新的示例中,PatternLayout
被 HTMLLayout
替代,它将日志格式化为 HTML 表格。您可以更改列的列表和顺序,以及表格的 CSS 样式。请参阅 HTMLLayout 文档以获取更多详细信息。
由于循环缓冲区的大小为 256,接收者应该看到一个包含 256 个方便地以 HTML 表格格式化的事件的电子邮件消息。请注意,此次运行的 chapters.appenders.mail.Email
应用程序生成了 10,000 个事件,其中只有最后的 256 个事件包含在发送的电子邮件中。
电子邮件客户端(如 Mozilla Thunderbird、Eudora 或 MS Outlook)对 HTML 电子邮件提供了合理良好的 CSS 支持。但是,它们有时会自动将 HTML 降级为纯文本。例如,在 Thunderbird 中查看 HTML 电子邮件,必须设置 "View→MessageBodyAs→Original HTML" 选项。Yahoo! Mail 对 HTML 电子邮件的支持,特别是其 CSS 支持非常好。然而,Gmail 尽管支持基本的 HTML 表格结构,但会忽略内部 CSS 格式。Gmail 支持内联 CSS 格式,但由于内联 CSS 会使结果输出过于庞大,HTMLLayout
不使用内联 CSS。
自定义缓冲区大小
默认情况下,SMTPAppender
的传出消息将包含 SMTPAppender
观察到的最后 256 条消息。如果您希望更改缓冲区大小,可以参考下面的示例配置。
示例:带有自定义缓冲区大小的 SMTPAppender
配置(logback-examples/src/main/resources/chapters/appenders/mail/customBufferSize.xml)
Legacy 版本
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<subject>%logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
<cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
<!-- 每封邮件只发送一条日志记录 -->
<bufferSize>1</bufferSize>
</cyclicBufferTracker>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
Canonical (1.3 版本)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.html.HTMLLayout"/>
<import class="ch.qos.logback.core.spi.CyclicBufferTracker"/>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<appender name="EMAIL" class="SMTPAppender">
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<subject>%logger{20} - %m</subject>
<layout class="HTMLLayout"/>
<cyclicBufferTracker class="CyclicBufferTracker">
<bufferSize>1</bufferSize>
</cyclicBufferTracker>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL"/>
</root>
</configuration>
触发事件
如果未设置 Evaluator
属性,SMTPAppender
默认使用一个 OnErrorEvaluator
实例,在遇到 ERROR 级别的事件时触发邮件传输。尽管在发生错误时触发发送邮件相对合理,但您可以通过提供不同的 EventEvaluator
接口实现来覆盖此默认行为。
SMTPAppender
通过调用其评估器的 evaluate()
方法将每个传入的事件提交给评估器,以检查事件是否应触发邮件或仅放置在循环缓冲区中。当评估器对其评估给出肯定答案时,将发送一封邮件。SMTPAppender
包含一个且仅包含一个评估器对象,该对象可以管理自己的内部状态。为了说明问题,下面列出的 CounterBasedEvaluator
类实现了一个事件评估器,其中每隔 1024 个事件触发一封邮件。
示例:EventEvaluator
实现,每隔 1024 个事件返回 true
(logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java)
package chapters.appenders.mail;
import ch.qos.logback.core.boolex.EvaluationException;
import ch.qos.logback.core.boolex.EventEvaluator;
import ch.qos.logback.core.spi.ContextAwareBase;
public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator {
static int LIMIT = 1024;
int counter = 0;
String name;
public boolean evaluate(Object event) throws NullPointerException,
EvaluationException {
counter++;
if (counter == LIMIT) {
counter = 0;
return true;
} else {
return false;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
请注意,该类扩展了 ContextAwareBase
并实现了 EventEvaluator
接口。这使用户可以专注于 EventEvaluator
的核心功能,而让基类提供通用功能。
设置 SMTPAppender
的 Evaluator
选项将其指示使用自定义的评估器。下面的配置文件将 SMTPAppender
附加到根记录器。此 appender 使用一个 CounterBasedEvaluator
实例作为其事件评估器。
例子:使用自定义评估器和缓冲区大小的 SMTPAppender(logback-examples/src/main/resources/chapters/appenders/mail/mail3.xml)
传统
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="chapters.appenders.mail.CounterBasedEvaluator" />
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<subject>%logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.html.HTMLLayout"/>
<import class="chapters.appenders.mail.CounterBasedEvaluator"/>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<appender name="EMAIL" class="SMTPAppender">
<evaluator class="CounterBasedEvaluator"/>
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<subject>%logger{20} - %m</subject>
<layout class="HTMLLayout"/>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL"/>
</root>
</configuration>
:::
基于标记的触发
尽管合理,但默认的触发策略,即每个 ERROR 级别事件触发一封发件箱可能会导致过多的邮件,使目标用户的邮箱混乱。Logback 附带了另一个触发策略,称为 OnMarkerEvaluator
。它基于标记。实质上,仅当事件被标记为用户指定的标记时,才会触发电子邮件。下一个示例将使这一点更清楚。
Marked_EMail 应用程序包含几个日志记录语句,其中一些是 ERROR 级别的。值得注意的语句包含一个标记。这是相关代码。
Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN");
logger.error(notifyAdmin,
"This is a serious error requiring the admin's attention",
new Exception("Just testing"));
下一个配置文件仅在存在带有 NOTIFY_ADMIN
或 TRANSACTION_FAILURE
标记的事件时触发传出电子邮件。
例子:使用 OnMarkerEvaluator
的 SMTPAppender
(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker.xml)
传统
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
<marker>NOTIFY_ADMIN</marker>
<!-- you specify add as many markers as you want -->
<marker>TRANSACTION_FAILURE</marker>
</evaluator>
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
<root>
<level value ="debug"/>
<appender-ref ref="EMAIL" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.html.HTMLLayout"/>
<import class="ch.qos.logback.classic.boolex.OnMarkerEvaluator"/>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<appender name="EMAIL" class="SMTPAppender">
<evaluator class="OnMarkerEvaluator">
<marker>NOTIFY_ADMIN</marker>
<marker>TRANSACTION_FAILURE</marker>
</evaluator>
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="HTMLLayout"/>
</appender>
<root>
<level value="debug"/>
<appender-ref ref="EMAIL"/>
</root>
</configuration>
:::
使用以下命令尝试一下:
java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
chapters.appenders.mail.Marked_EMail src/main/java/chapters/appenders/mail/mailWithMarker.xml
基于标记的触发与 JaninoEventEvaluator
请注意,我们可以使用更通用的 JaninoEventEvaluator
代替基于标记的 OnMarkerEvaluator
。例如,下面的配置文件使用 JaninoEventEvaluator
而不是 OnMarkerEvaluator
,但与先前的配置文件相同。
示例:带有 JaninoEventEvaluator
的 SMTPAppender
(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker_Janino.xml)
传统风格
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
<expression>
(marker != null) &&
(marker.contains("NOTIFY_ADMIN") || marker.contains("TRANSACTION_FAILURE"))
</expression>
</evaluator>
<smtpHost>${smtpHost}</smtpHost>
<to>${to}</to>
<from>${from}</from>
<layout class="ch.qos.logback.classic.html.HTMLLayout"/>
</appender>
</configuration>
规范化(1.3 版本)
无法转换
身份验证/STARTTLS/SSL
SMTPAppender
支持使用明文用户密码以及 STARTTLS 和 SSL 协议进行身份验证。请注意,STARTTLS 与 SSL 的不同之处在于,在 STARTTLS 中,连接最初是非加密的,只有在客户端发出 STARTTLS
命令(如果服务器支持)后,连接才会切换到 SSL。在 SSL 模式下,连接从一开始就是加密的。
用于 Gmail 的 SMTPAppender 配置(SSL)
下面的示例展示了如何使用 SSL 协议配置 SMTPAppender
以将日志发送到 Gmail。
示例:使用 SSL 将日志发送到 Gmail 的 SMTPAppender
(logback-examples/src/main/resources/chapters/appenders/mail/gmailSSL.xml)
传统风格
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>465</smtpPort>
<SSL>true</SSL>
<username>YOUR_USERNAME@gmail.com</username>
<password>YOUR_GMAIL_PASSWORD</password>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to> <!-- 可以添加额外的目标邮箱 -->
<from>YOUR_USERNAME@gmail.com</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level %logger{35} - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
规范化(1.3 版本)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<import class="ch.qos.logback.classic.PatternLayout"/>
<appender name="EMAIL" class="SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>465</smtpPort>
<sSL>true</sSL>
<username>YOUR_USERNAME@gmail.com</username>
<password>YOUR_GMAIL_PASSWORD</password>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to>
<from>YOUR_USERNAME@gmail.com</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="PatternLayout">
<pattern>%date %-5level %logger{35} - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL"/>
</root>
</configuration>
用于 Gmail 的 SMTPAppender(STARTTLS)
下面的示例展示了如何为 STARTTLS 协议配置 SMTPAppender
,以使用 Gmail。
示例:使用 STARTTLS 将 SMTPAppender
配置为 Gmail(logback-examples/src/main/resources/chapters/appenders/mail/gmailSTARTTLS.xml)
旧版配置
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>587</smtpPort>
<STARTTLS>true</STARTTLS>
<username>YOUR_USERNAME@gmail.com</username>
<password>YOUR_GMAIL_xPASSWORD</password>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to> <!-- 可以添加其他目标地址 -->
<from>YOUR_USERNAME@gmail.com</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date %-5level %logger - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL" />
</root>
</configuration>
规范配置(1.3 版)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<import class="ch.qos.logback.classic.PatternLayout"/>
<appender name="EMAIL" class="SMTPAppender">
<smtpHost>smtp.gmail.com</smtpHost>
<smtpPort>587</smtpPort>
<sTARTTLS>true</sTARTTLS>
<username>YOUR_USERNAME@gmail.com</username>
<password>YOUR_GMAIL_xPASSWORD</password>
<to>EMAIL-DESTINATION</to>
<to>ANOTHER_EMAIL_DESTINATION</to>
<from>YOUR_USERNAME@gmail.com</from>
<subject>TESTING: %logger{20} - %m</subject>
<layout class="PatternLayout">
<pattern>%date %-5level %logger - %message%n</pattern>
</layout>
</appender>
<root level="DEBUG">
<appender-ref ref="EMAIL"/>
</root>
</configuration>
带有 MDCDiscriminator 的 SMTPAppender
如前所述,通过指定除默认分配器以外的分配器,SMTPAppender
将生成包含与特定用户、用户会话或客户端 IP 地址相关的事件的电子邮件消息,具体取决于指定的分配器。
下面的示例演示了在 MDC 键为 "req.remoteHost"
的情况下使用 MDCBasedDiscriminator
以及假设该键包含访问虚构应用程序的远程主机的 IP 地址。在 Web 应用程序中,您可以使用 MDCInsertingServletFilter
来填充 MDC 值。
示例:带有 MDCBasedDiscriminator
的 SMTPAppender
(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml)
旧版配置
<configuration>
<appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost>
<to>EMAIL-DESTINATION</to>
<from>SENDER-EMAIL</from>
<discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator">
<key>req.remoteHost</key>
<defaultValue>default</defaultValue>
</discriminator>
<subject>${HOSTNAME} -- %X{req.remoteHost} %msg"</subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg</pattern>
</layout>
</appender>
<root>
<level level="DEBUG"/>
<appender-ref ref="EMAIL" />
</root>
</configuration>
规范配置(1.3 版)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.html.HTMLLayout"/>
<import class="ch.qos.logback.classic.net.SMTPAppender"/>
<import class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"/>
<appender name="EMAIL" class="SMTPAppender">
<smtpHost>ADDRESS-OF-YOUR-SMTP-HOST</smtpHost>
<to>EMAIL-DESTINATION</to>
<from>SENDER-EMAIL</from>
<discriminator class="MDCBasedDiscriminator">
<key>req.remoteHost</key>
<defaultValue>default</defaultValue>
</discriminator>
<subject>${HOSTNAME} -- %X{req.remoteHost} %msg"</subject>
<layout class="HTMLLayout">
<pattern>%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg</pattern>
</layout>
</appender>
<root>
<appender-ref ref="EMAIL"/>
</root>
</configuration>
因此,由 SMTPAppender
生成的每封外发邮件都属于一个 唯一 的远程主机,极大地方便了问题诊断。
非常繁忙的系统中的缓冲区管理
内部,每个鉴别器返回的不同值将导致创建一个新的循环缓冲区。然而,最多会维护 maxNumberOfBuffers
(默认为 64)个缓冲区。每当缓冲区数量上升超过 maxNumberOfBuffers
时,最近更新的缓冲区将自动丢弃。作为第二个安全措施,任何在过去 30 分钟内未更新的缓冲区也将自动丢弃。
在每分钟服务大量交易的系统中,允许 maxNumberOfBuffers
(默认为 64)的数量往往会导致发送邮件中的事件数量不必要地小。事实上,在存在大量交易的情况下,将有多个与同一交易相关联的缓冲区,因为缓冲区将为相同的鉴别器值(或交易)连续被删除和重建。请注意,即使是这样非常繁忙的系统,循环缓冲区的最大数量也受到 maxNumberOfBuffers
的限制。
为了避免这种类似于哑铃的效应,SMTPAppender
将在看到标记为 "FINALIZE_SESSION"
的事件时释放与给定鉴别器键相关联的缓冲区。这将导致在每个交易结束时丢弃相应的缓冲区。您可以放心地将 maxNumberOfBuffers
的值增加到更大的值,例如 512 或 1024,而不必担心内存耗尽。
有三种不同但相互补充的机制一起管理循环缓冲区。它们确保任何特定时刻只保留相关的缓冲区,即使在非常繁忙的系统中也是如此。
DBAppender
DBAppender
会以与 Java 编程语言无关的格式将日志事件插入到三个数据库表中。
截至 logback 版本 1.2.8,DBAppender
不再随 logback-classic 一起提供。但是,logback-classic 的 DBAppender
可在以下 Maven 坐标下使用:
ch.qos.logback.db:logback-classic-db:1.2.11.1
这三张表分别是 logging_event、logging_event_property 和 logging_event_exception。在使用 DBAppender
之前,它们必须存在。Logback 附带了 SQL 脚本来创建这些表。它们可以在 logback-classic/src/main/java/ch/qos/logback/classic/db/script 文件夹下找到。每种流行的数据库系统都有一个特定的脚本。如果您使用的数据库系统的脚本丢失,则应该很容易编写一个,以现有脚本为例。如果您将它们发送给我们,我们将很乐意在未来的版本中包含缺少的脚本。
如果您的 JDBC 驱动程序支持 JDBC 3.0 规范中引入的 getGeneratedKeys
方法,并假定您已经创建了上述适当的数据库表,则不需要采取任何其他步骤。否则,必须有适用于您的数据库系统的 SQLDialect
。目前,logback 具有适用于 H2、HSQL、MS SQL Server、MySQL、Oracle、PostgreSQL、SQLLite 和 Sybase 的方言。
下表总结了数据库类型及其对 getGeneratedKeys()
方法的支持。
RDBMS | 测试版本 | 测试的 JDBC 驱动版本 | 支持 getGeneratedKeys() 方法 | logback 是否提供方言 |
---|---|---|---|---|
DB2 | 未测试 | 未测试 | 未知 | 否 |
H2 | 1.2.132 | - | 未知 | 是 |
HSQL | 1.8.0.7 | - | 否 | 是 |
Microsoft SQL Server | 2005 | 2.0.1008.2 (sqljdbc.jar) | 是 | 是 |
MySQL | 5.0.22 | 5.0.8 (mysql-connector.jar) | 是 | 是 |
PostgreSQL | 8.x | 8.4-701.jdbc4 | 否 | 是 |
Oracle | 10g | 10.2.0.1 (ojdbc14.jar) | 是 | 是 |
SQLLite | 3.7.4 | - | 未知 | 是 |
Sybase SQLAnywhere | 10.0.1 | - | 未知 | 是 |
实验表明,在“标准”个人电脑上,将单个事件写入数据库大约需要 10 毫秒。如果使用池化连接,则此数字会降至约 1 毫秒。请注意,大多数 JDBC 驱动程序已经提供了连接池支持。
配置 logback 以使用 DBAppender
可以通过几种不同的方式完成,具体取决于用于连接到数据库的工具和数据库本身。配置 DBAppender
的关键问题是设置其 ConnectionSource
对象,我们很快就会发现。
一旦为您的数据库配置了 DBAppender
,日志事件就会被发送到指定的数据库。如前所述,logback 使用三个表存储日志事件数据。
logging_event 表包含以下字段:
字段 | 类型 | 描述 |
---|---|---|
timestamp | big int | 日志事件创建时有效的时间戳。 |
formatted_message | text | 在将对象与消息一起传递后,使用 org.slf4j.impl.MessageFormatter 格式化后添加到日志事件中的消息。 |
logger_name | varchar | 发出日志请求的记录器的名称。 |
level_string | varchar | 日志事件的级别。 |
reference_flag | smallint | 此字段由 logback 用于标识具有异常或 MDC 属性值的日志事件。它的值由 ch.qos.logback.classic.db.DBHelper 计算。包含 MDC 或 Context 属性的日志事件具有标志数为 1。包含异常的日志事件具有标志数为 2。同时包含两个元素的日志事件具有标志数为 3。 |
caller_filename | varchar | 发出日志请求的文件的名称。 |
caller_class | varchar | 发出日志请求的类。 |
caller_method | varchar | 发出日志请求的方法的名称。 |
caller_line | char | 发出日志请求的行号。 |
event_id | int | 日志事件的数据库 ID。 |
logging_event_property 用于存储包含在 MDC
或 Context
中的键和值。它包含以下字段:
字段 | 类型 | 描述 |
---|---|---|
event_id | int | 日志事件的数据库 ID。 |
mapped_key | varchar | MDC 属性的键 |
mapped_value | text | MDC 属性的值 |
logging_event_exception 表包含以下字段:
字段 | 类型 | 描述 |
---|---|---|
event_id | int | 日志事件的数据库 ID。 |
i | smallint | 完整堆栈跟踪中的行索引。 |
trace_line | varchar | 相应的行 |
为了更直观地展示 DBAppender
的工作,下面是一个由 DBAppender
提供内容的 MySQL 数据库的屏幕截图。
logging_event 表:
logging_event_exception 表:
logging_event_property 表:
ConnectionSource
ConnectionSource
接口提供了一种可插拔的方式,用于透明地为需要使用 java.sql.Connection
的 logback 类获取 JDBC 连接。目前有三种 ConnectionSource
的实现,分别是 DataSourceConnectionSource
、DriverManagerConnectionSource
和 JNDIConnectionSource
。
我们将首先介绍使用 DriverManagerConnectionSource
和 MySQL 数据库的配置示例。下面是一个配置文件示例。
示例:DBAppender
配置 (logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-driverManager.xml)
<configuration>
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://host_name:3306/database_name</url>
<user>username</user>
<password>password</password>
</connectionSource>
</appender>
<root level="DEBUG" >
<appender-ref ref="DB" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.core.db.DriverManagerConnectionSource"/>
<import class="ch.qos.logback.classic.db.DBAppender"/>
<appender name="DB" class="DBAppender">
<connectionSource class="DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://host_name:3306/database_name</url>
<user>username</user>
<password>password</password>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB"/>
</root>
</configuration>
必须声明正确的驱动程序。这里使用的是 com.mysql.jdbc.Driver
类。URL 必须以 jdbc:mysql:// 开头。
DriverManagerConnectionSource
是 ConnectionSource
的一个实现,它根据连接 URL 以传统的 JDBC 方式获取连接。
请注意,该类将为每次调用 getConnection()
建立一个新的 Connection
。建议您要么使用本地支持连接池的 JDBC 驱动程序,要么创建自己的 ConnectionSource
实现,以利用您已经使用的任何池化机制。如果您可以访问支持 javax.sql.DataSource
的 JNDI 实现(例如在 J2EE 应用服务器中),请参阅下面的 JNDIConnectionSource
。
使用 DataSource
连接到数据库相当类似。现在的配置使用了 DataSourceConnectionSource
,它是一个基于 javax.sql.DataSource
的推荐 JDBC 方式来获取 Connection
的 ConnectionSource
的实现。
示例:DBAppender
配置 (logback-examples/src/main/resources/chapters/appenders/db/append-with-datasource.xml)
<configuration debug="true">
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="${dataSourceClass}">
<!-- Joran cannot substitute variables
that are not attribute values. Therefore, we cannot
declare the next parameter like the others.
-->
<param name="${url-key:-url}" value="${url_value}"/>
<serverName>${serverName}</serverName>
<databaseName>${databaseName}</databaseName>
</dataSource>
<user>${user}</user>
<password>${password}</password>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration debug="true">
<import class="${dataSourceClass}"/>
<import class="ch.qos.logback.core.db.DataSourceConnectionSource"/>
<import class="ch.qos.logback.classic.db.DBAppender"/>
<appender name="DB" class="DBAppender">
<connectionSource class="DataSourceConnectionSource">
<dataSource>
<${url-key:-url}>${url_value}</${url-key:-url}>
<serverName>${serverName}</serverName>
<databaseName>${databaseName}</databaseName>
</dataSource>
<user>${user}</user>
<password>${password}</password>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB"/>
</root>
</configuration>
请注意,在此配置示例中,我们大量使用了替代变量。当连接详细信息必须集中在单个配置文件中,并由 logback 和其他框架共享时,它们有时非常方便。
JNDIConnectionSource
JNDIConnectionSource
是 logback 中提供的另一个 ConnectionSource
实现。正如其名称所示,它从 JNDI 中检索一个 javax.sql.DataSource
,然后利用它来获取一个 java.sql.Connection
实例。JNDIConnectionSource
主要设计用于在 J2EE 应用服务器内部或应用服务器客户端中使用,假设应用服务器支持远程访问 javax.sql.DataSource
。因此,可以利用连接池和应用服务器提供的其他好处。更重要的是,您的应用程序将变得更加干净,因为在 logback.xml 中不再需要定义 DataSource
。
例如,这里是针对 Tomcat 的配置片段。虽然任何受支持的数据库系统(如上面列出的)都可以工作,但它假定 PostgreSQL 作为数据库。
<Context docBase="/path/to/app.war" path="/myapp">
...
<Resource name="jdbc/logging"
auth="Container"
type="javax.sql.DataSource"
username="..."
password="..."
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://localhost/..."
maxActive="8"
maxIdle="4"/>
...
</Context>
一旦在 J2EE 服务器中定义了一个 DataSource
,就可以在您的 logback 配置文件中轻松引用它,如下例所示。
示例:通过 JNDIConnectionSource
配置 DBAppender
(logback-examples/src/main/resources/chapters/appenders/db/append-via-jndi.xml)
<configuration debug="true">
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource">
<!-- 请注意 "java:comp/env/" 前缀 -->
<jndiLocation>java:comp/env/jdbc/logging</jndiLocation>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration debug="true">
<import class="ch.qos.logback.classic.db.DBAppender"/>
<import class="ch.qos.logback.core.db.JNDIConnectionSource"/>
<appender name="DB" class="DBAppender">
<connectionSource class="JNDIConnectionSource">
<jndiLocation>java:comp/env/jdbc/logging</jndiLocation>
</connectionSource>
</appender>
<root level="INFO">
<appender-ref ref="DB"/>
</root>
</configuration>
请注意,该类将使用无参数构造函数获取一个 javax.naming.InitialContext
。当在 J2EE 环境中执行时,这通常会起作用。当不在 J2EE 环境中时,请确保按照您的 JNDI 提供程序文档的描述提供一个 jndi.properties 文件。
连接池
日志事件可能会以相当快的速度创建。为了跟上必须插入数据库的事件流,建议使用 DBAppender
进行连接池操作。
实验证明,使用连接池与 DBAppender
可以大大提高性能。通过以下配置文件,日志事件被发送到一个 MySQL 数据库,且没有进行任何连接池操作。
示例:没有连接池的 DBAppender
配置(logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-datasource.xml)
<configuration>
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
<serverName>${serverName}</serverName>
<port>${port$</port>
<databaseName>${dbName}</databaseName>
<user>${user}</user>
<password>${pass}</password>
</dataSource>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.core.db.DataSourceConnectionSource"/>
<import class="ch.qos.logback.classic.db.DBAppender"/>
<import class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"/>
<appender name="DB" class="DBAppender">
<connectionSource class="DataSourceConnectionSource">
<dataSource class="MysqlDataSource">
<serverName>${serverName}</serverName>
<port>${port$</port>
<databaseName>${dbName}</databaseName>
<user>${user}</user>
<password>${pass}</password>
</dataSource>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB"/>
</root>
</configuration>
通过这个配置文件,向 MySQL 数据库发送 500 个日志事件需要长达 5 秒,即每个请求需要 10 毫秒。这个数字在处理大型应用程序时是不可接受的。
使用 DBAppender
与连接池需要一个专用的外部库。以下示例使用 c3p0。为了能够使用 c3p0,必须下载它并将 c3p0-VERSION.jar 放置在类路径中。
示例:带有连接池的 DBAppender
配置(logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-datasource-and-pooling.xml)
<configuration>
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource
class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}</jdbcUrl>
<user>${user}</user>
<password>${password}</password>
</dataSource>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="com.mchange.v2.c3p0.ComboPooledDataSource"/>
<import class="ch.qos.logback.core.db.DataSourceConnectionSource"/>
<import class="ch.qos.logback.classic.db.DBAppender"/>
<appender name="DB" class="DBAppender">
<connectionSource class="DataSourceConnectionSource">
<dataSource class="ComboPooledDataSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}</jdbcUrl>
<user>${user}</user>
<password>${password}</password>
</dataSource>
</connectionSource>
</appender>
<root level="DEBUG">
<appender-ref ref="DB"/>
</root>
</configuration>
通过这个新的配置,向上述的 MySQL 数据库发送 500 个日志请求大约需要 0.5 秒,平均每个请求 1 毫秒,性能提高了十倍。
SyslogAppender
syslog 协议是一个非常简单的协议:syslog 发送方将一个小消息发送到 syslog 接收方。接收方通常称为 syslog 守护进程 或 syslog 服务器。Logback 可以向远程 syslog 守护进程发送消息,使用 SyslogAppender
实现。
下面是可以传递给 SyslogAppender
的属性。
日志事件的 syslog 严重程度从日志事件的级别转换而来。DEBUG 级别转换为 7,INFO 转换为 6,WARN 转换为 4,ERROR 转换为 3。
由于 syslog 请求的格式遵循相当严格的规则,因此没有布局可与 SyslogAppender
一起使用。但是,使用 suffixPattern
选项可以让用户显示他想要的任何信息。
下面是使用 SyslogAppender
的示例配置。
示例:SyslogAppender
配置(logback-examples/src/main/resources/chapters/appenders/conf/logback-syslog.xml)
<configuration>
<appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
<syslogHost>remote_home</syslogHost>
<facility>AUTH</facility>
<suffixPattern>[%thread] %logger %msg</suffixPattern>
</appender>
<root level="DEBUG">
<appender-ref ref="SYSLOG" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration>
<import class="ch.qos.logback.classic.net.SyslogAppender"/>
<appender name="SYSLOG" class="SyslogAppender">
<syslogHost>remote_home</syslogHost>
<facility>AUTH</facility>
<suffixPattern>[%thread] %logger %msg</suffixPattern>
</appender>
<root level="DEBUG">
<appender-ref ref="SYSLOG"/>
</root>
</configuration>
在测试此配置时,应验证远程 syslog 守护进程是否接受来自外部源的请求。经验表明,默认情况下,syslog 守护进程通常会拒绝通过网络连接发送的请求。
SiftingAppender
正如其名,SiftingAppender
可以根据给定的运行时属性将日志分离(或筛选)。例如,SiftingAppender
可以根据用户会话将日志事件分离,使不同用户生成的日志进入不同的日志文件,每个用户一个日志文件。
属性名称 | 类型 | 描述 |
---|---|---|
timeout | Duration | 超过超时持续时间未访问的嵌套 appender 被视为过期。过期的 appender 将被 SiftingAppender 关闭并移除。timeout 的默认值为 30 分钟。 |
maxAppenderCount | integer | SiftingAppender 可以创建和跟踪的最大嵌套 appender 数量。maxAppenderCount 的默认值为 Integer.MAX_VALUE 。 |
SiftingAppender
通过动态创建嵌套 appender 来实现此功能。嵌套 appender 是根据配置文件中的模板创建的(在 <sift>
元素中指定)。SiftingAppender
负责管理子 appender 的生命周期。例如,SiftingAppender
将自动关闭并删除任何过期的 appender。当超过 timeout
参数指定的持续时间后,嵌套 appender 被视为过期。
在处理日志事件时,SiftingAppender
会选择一个子 appender 进行委托。选择标准由一个鉴别器(Discriminator
)在运行时计算。用户可以通过 Discriminator
来指定选择标准。接下来我们来看一个示例。
示例
SiftExample 应用程序记录一条消息,表示应用程序已启动。然后它将 MDC 键 "userid" 设置为 "Alice" 并记录一条消息。以下是关键代码:
logger.debug("Application started");
MDC.put("userid", "Alice");
logger.debug("Alice says hello");
配置文件的模板演示了如何使用 SiftingAppender
。
示例:SiftingAppender
配置文件(logback-examples/src/main/resources/chapters/appenders/sift/byUserid.xml)
<configuration>
<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
<!-- in the absence of the class attribute, it is assumed that the
desired discriminator type is
ch.qos.logback.classic.sift.MDCBasedDiscriminator -->
<discriminator>
<key>userid</key>
<defaultValue>unknown</defaultValue>
</discriminator>
<sift>
<appender name="FILE-${userid}" class="ch.qos.logback.core.FileAppender">
<file>${userid}.log</file>
<append>false</append>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d [%thread] %level %mdc %logger{35} -%kvp -%msg%n</pattern>
</layout>
</appender>
</sift>
</appender>
<root level="DEBUG">
<appender-ref ref="SIFT" />
</root>
</configuration>
如果没有指定 class
属性,则假定鉴别器类型是 MDCBasedDiscriminator
。区分值是与 key
属性给定的 MDC 值关联的值。然而,如果该 MDC 值为 null
,则使用 defaultValue
作为区分值。
SiftingAppender
在引用和配置子 appender 方面具有独特的能力。在上面的示例中,SiftingAppender
将创建多个 FileAppender
实例,每个 FileAppender
实例由与 "userid" MDC 键关联的值标识。每当 "userid" MDC 键被赋予新值时,将从头开始构建一个新的 FileAppender
实例。SiftingAppender
跟踪它创建的 appender。30 分钟内未使用的 appender 将自动关闭和丢弃。
变量导出
仅拥有不同的 appender 实例是不够的;每个实例必须输出到不同的目标资源。为了允许这种区分,模板中传递给鉴别器的键(在上面的示例中为 "userid")被导出,并成为 变量。因此,可以使用该变量来区分给定子 appender 使用的实际资源。
使用上述显示的 "byUserid.xml" 配置文件运行 SiftExample
应用程序将会产生两个不同的日志文件,"unknown.log" 和 "Alice.log"。
局部作用域变量
自版本 1.0.12 起,在配置文件中在本地作用域内定义的属性将对嵌套 appender 可用。此外,您可以 定义变量 或 动态计算 在 <sift>
元素内部。还支持在 <sift>
元素的外部和内部定义的部分组合变量。
设置合适的超时时间
对于某些类型的应用程序来说,设置超时参数可能会很困难。如果超时时间太短,嵌套的 appender 可能会在几秒后被移除,然后重新创建。这种现象称为 trashing。如果超时时间太长,并且 appender 被快速连续地创建,可能会耗尽资源。同样,将 maxAppenderCount
设置得太低也可能导致 trashing。
在许多情况下,更容易找到代码中不再需要嵌套 appender 的位置。如果存在这样一个位置,即使是大概的,可以使用 FINALIZE_SESSION
标记从该位置记录日志。每当 SiftingAppender
看到标记为 FINALIZE_SESSION
的日志事件,它将结束关联的嵌套 appender 的生命周期。当达到生命周期末期时,嵌套 appender 将持续几秒钟以处理任何延迟到来的事件(如果有),然后关闭。
import org.slf4j.Logger;
import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER;
void job(String jobId) {
MDC.put("jobId", jobId);
logger.info("Starting job.");
... 执行工作需要做的事情
// 会导致嵌套 appender 达到生命周期末期。它将持续几秒钟。
logger.info(FINALIZE_SESSION_MARKER, "About to end the job");
try {
.. 执行清理工作
} catch(Exception e);
// 这条日志语句将由持续存在的 appender 处理。
// 不会创建新的 appender。
logger.error("unexpected error while cleaning up", e);
}
}
AsyncAppender
AsyncAppender
异步记录 ILoggingEvent
。它仅充当事件分发器,因此必须引用另一个 appender 才能发挥作用。
默认情况下,如果占用 80% 的话会有丢失
AsyncAppender
在 BlockingQueue
中缓冲事件。由 AsyncAppender
创建的工作线程从队列头部获取事件,并将其分派到与 AsyncAppender
相关联的单个 appender。请注意,默认情况下,如果 AsyncAppender
的队列占据了 80% 的空间,它将丢弃级别为 TRACE、DEBUG 和 INFO 的事件。这种策略对性能有着非常有利的影响,但会造成事件丢失。
应用停止/重部署
在应用程序关闭或重新部署时,必须停止 AsyncAppender
以停止和回收工作线程,并从队列中刷新日志事件。这可以通过 停止 LoggerContext
来实现,它将关闭所有 appender,包括任何 AsyncAppender
实例。AsyncAppender
将等待工作线程在 maxFlushTime
指定的超时时间内刷新。如果在关闭 LoggerContext
时发现排队的事件被丢弃,可能需要增加超时时间。指定 maxFlushTime
为 0 将强制 AsyncAppender
在返回停止方法之前等待所有排队的事件被刷新。
关闭后清理
根据 JVM 关闭的模式,处理排队事件的工作线程可能会被中断,导致事件被滞留在队列中。这通常发生在 LoggerContext
未被干净地停止或者 JVM 在典型控制流之外终止时。为了避免在这些情况下中断工作线程,可以向 JVM 运行时插入一个关闭钩子,该钩子在 JVM 关闭被启动后 正确停止 LoggerContext
。当其他关闭钩子尝试记录事件时,关闭钩子也可以是清除关闭 Logback 的首选方法。
这是 AsyncAppender
允许的属性列表:
属性名 | 类型 | 描述 |
---|---|---|
queueSize | int | 阻塞队列的最大容量。默认情况下,queueSize 设置为 256。 |
discardingThreshold | int | 默认情况下,当阻塞队列剩余容量为 20% 时,将丢弃级别为 TRACE、DEBUG 和 INFO 的事件,仅保留级别为 WARN 和 ERROR 的事件。要保留所有事件,请将 discardingThreshold 设置为 0。 |
includeCallerData | boolean | 提取调用者数据可能相当昂贵。为了提高性能,默认情况下,与事件相关联的调用者数据在添加到事件队列时不会被提取。默认情况下,只会复制像线程名称和 MDC 这样的“廉价”数据。您可以通过将 includeCallerData 属性设置为 true 来指示此 appender 包括调用者数据。 |
maxFlushTime | int | 根据队列深度和对应 appender 的延迟,AsyncAppender 可能需要花费不可接受的时间来完全刷新队列。当 LoggerContext 被停止时,AsyncAppender 的 stop 方法将等待工作线程完成的时间最长达到此超时时间。使用 maxFlushTime 指定以毫秒为单位的最大队列刷新超时时间。在此窗口内无法处理的事件将被丢弃。该值的语义与 Thread.join(long) 相同。 |
neverBlock | boolean | 如果为 false (默认值),appender 将在向已满队列添加事件时阻塞,而不是丢失消息。如果设置为 true ,appender 将仅丢弃消息,而不会阻塞您的应用程序。 |
默认情况下,事件队列配置为最大容量为 256 个事件。如果队列已满,则应用程序线程将被阻止记录新事件,直到工作线程有机会分派一个或多个事件。当队列不再处于最大容量时,应用程序线程就能够开始再次记录事件。因此,在 appender 在其事件缓冲区的容量处于或接近最大容量时,异步记录实际上变得伪同步。这未必是一件坏事。该 appender 被设计为允许应用程序继续运行,尽管在日志事件压力缓解之前记录事件需要稍微更多的时间。
针对最大应用程序吞吐量最佳调整 appender 事件队列大小的优化取决于几个因素。以下任何一个或所有因素都可能导致表现出伪同步行为:
- 大量的应用程序线程
- 每个应用程序调用的大量日志事件
- 每个日志事件的大量数据
- 子 appender 的高延迟
为了保持事物顺利进行,增加队列的大小通常会有所帮助,但会以减少应用程序可用堆空间为代价。
丢失行为
鉴于上述讨论并为了减少阻塞,AsyncAppender
默认情况下,当队列剩余容量低于 20% 时,将丢弃级别为 TRACE、DEBUG 和 INFO 的事件,仅保留级别为 WARN 和 ERROR 的事件。该策略确保处理日志事件时不会阻塞(因此性能优秀),但会以低于 20% 容量时丢失级别为 TRACE、DEBUG 和 INFO 的事件为代价。通过将 discardingThreshold
属性设置为 0(零)可以避免事件丢失。
示例:AsyncAppender
配置(logback-examples/src/main/resources/chapters/appenders/conc/logback-async.xml)
传统
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myapp.log</file>
<encoder>
<pattern>%logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
</appender>
<root level="DEBUG">
<appender-ref ref="ASYNC" />
</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.classic.AsyncAppender"/>
<import class="ch.qos.logback.core.FileAppender"/>
<appender name="FILE" class="FileAppender">
<file>myapp.log</file>
<encoder class="PatternLayoutEncoder">
<pattern>%logger{35} -%kvp -%msg%n</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="AsyncAppender">
<appender-ref ref="FILE"/>
</appender>
<root level="DEBUG">
<appender-ref ref="ASYNC"/>
</root>
</configuration>
编写自己的 Appender
您可以通过继承 AppenderBase
来轻松地编写自己的 appender。它处理了大多数 appender 共享的过滤器、状态消息和其他功能。派生类只需要实现一个方法,即 append(Object eventObject)
。
下面列出的 CountingConsoleAppender
在控制台上附加了一定数量的事件。达到限制后,它将关闭。它使用 PatternLayoutEncoder
来格式化事件,并接受一个名为 limit
的参数。因此,除了 append(Object eventObject)
之外,还需要几个方法。如下所示,这些参数由 logback 的各种配置机制自动处理。
示例 4:CountingConsoleAppender
(logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java)
package chapters.appenders;
import java.io.IOException;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
public class CountingConsoleAppender extends AppenderBase<ILoggingEvent> {
static int DEFAULT_LIMIT = 10;
int counter = 0;
int limit = DEFAULT_LIMIT;
PatternLayoutEncoder encoder;
public void setLimit(int limit) {
this.limit = limit;
}
public int getLimit() {
return limit;
}
@Override
public void start() {
if (this.encoder == null) {
addError("No encoder set for the appender named ["+ name +"].");
return;
}
try {
encoder.init(System.out);
} catch (IOException e) {
}
super.start();
}
public void append(ILoggingEvent event) {
if (counter >= limit) {
return;
}
// output the events as formatted by our layout
try {
this.encoder.doEncode(event);
} catch (IOException e) {
}
// prepare for next event
counter++;
}
public PatternLayoutEncoder getEncoder() {
return encoder;
}
public void setEncoder(PatternLayoutEncoder encoder) {
this.encoder = encoder;
}
}
start()
方法检查是否存在 PatternLayoutEncoder
。如果未设置编码器,则 appender 无法启动并发出错误消息。
这个自定义 appender 说明了两点:
- 遵循 setter/getter JavaBeans 约定的所有属性都由 logback 配置器透明处理。在 logback 配置期间自动调用的
start()
方法负责验证 appender 的各个属性是否设置并且是否一致。 AppenderBase.doAppend()
方法调用其派生类的append()
方法。实际的输出操作发生在append()
方法中。特别是在这个方法中,appender 通过调用它们的布局来格式化事件。
CountingConsoleAppender
可以像其他 appender 一样进行配置。有关示例配置文件 logback-examples/src/main/resources/chapters/appenders/countingConsole.xml 的示例,请参见。
Logback Access
在 logback-classic 中找到的大多数 appender 也在 logback-access 中有相应的版本。它们的工作方式与 logback-classic 的对应 appender 基本相同。在接下来的部分,我们将介绍它们的使用方法。
SocketAppender 和 SSLSocketAppender
SocketAppender
设计用于通过传输序列化的 AccessEvent
对象来记录到远程实体。就访问事件而言,远程日志记录是非侵入性的。在反序列化后,可以像本地生成的事件一样记录该事件。
SSLSocketAppender
扩展了基本的 SocketAppender
,允许通过安全套接字层(SSL)记录到远程实体。
access 的 SocketAppender
的属性与 classic 的 SocketAppender
可用属性相同。
ServerSocketAppender 和 SSLServerSocketAppender
与 SocketAppender
类似,ServerSocketAppender
设计用于通过传输序列化的 AccessEvent
对象来记录到远程实体。但是,当使用 ServerSocketAppender
时,appender 充当服务器,被动地侦听 TCP 套接字,等待来自感兴趣的客户端的入站连接。传递给 appender 的日志事件将分发到所有连接的客户端。
SSLSocketAppender
扩展了基本的 ServerSocketAppender
,允许通过安全套接字层(SSL)记录到远程实体。
access 的 ServerSocketAppender
的属性与 classic 的 ServerSocketAppender
可用属性相同。
SMTPAppender
Access 的 SMTPAppender
与其经典对应项的工作方式相同。但是,评估器选项则有所不同。默认情况下,SMTPAppender
使用一个 URLEvaluator
对象。该评估器包含一个 URL 列表,用于与当前请求的 URL 进行匹配。当其中一个页面被 URLEvaluator
请求时,SMTPAppender
会发送一封电子邮件。
以下是在访问环境中配置 SMTPAppender
的示例配置。
示例:SMTPAppender
配置 (logback-examples/src/main/resources/chapters/appenders/conf/access/logback-smtp.xml)
<appender name="SMTP"
class="ch.qos.logback.access.net.SMTPAppender">
<layout class="ch.qos.logback.access.html.HTMLLayout">
<pattern>%h%l%u%t%r%s%b</pattern>
</layout>
<Evaluator class="ch.qos.logback.access.net.URLEvaluator">
<URL>url1.jsp</URL>
<URL>directory/url2.html</URL>
</Evaluator>
<from>sender_email@host.com</from>
<smtpHost>mail.domain.com</smtpHost>
<to>recipient_email@host.com</to>
</appender>
通过这种触发电子邮件的方式,用户可以选择对特定流程中重要的页面进行监控。例如,当访问了此类页面时,将发送一封带有先前访问过的页面以及用户想要包含在电子邮件中的任何信息的电子邮件。
DBAppender
DBAppender
用于将访问事件插入数据库。
在 logback 版本 1.2.8 中,DBAppender
不再随 logback-access 一起提供。然而,logback-access 的 DBAppender
可以通过以下 Maven 坐标获得:
ch.qos.logback.db:logback-access-db:1.2.11.1
DBAppender
使用两个表:access_event 和 access_event_header。在使用 DBAppender
之前,这两个表必须存在。Logback 附带了用于创建这些表的 SQL 脚本。这些脚本可以在 logback-access/src/main/java/ch/qos/logback/access/db/script 目录中找到。对于最常见的数据库系统,每种系统都有一个专门的脚本。如果您特定类型的数据库系统的脚本丢失,那么编写一个脚本应该非常容易,只需参考现有脚本之一即可。欢迎您贡献这些缺失的脚本到该项目。
access_event 表的字段如下所示:
字段 | 类型 | 描述 |
---|---|---|
timestamp | big int | 访问事件创建时的时间戳。 |
requestURI | varchar | 请求的 URI。 |
requestURL | varchar | 请求的 URL。这是由请求方法、请求 URI 和请求协议组成的字符串。 |
remoteHost | varchar | 远程主机的名称。 |
remoteUser | varchar | 远程用户的名称。 |
remoteAddr | varchar | 远程 IP 地址。 |
protocol | varchar | 请求协议,如 HTTP 或 HTTPS。 |
method | varchar | 请求方法,通常为 GET 或 POST。 |
serverName | varchar | 发出请求的服务器名称。 |
event_id | int | 访问事件的数据库 ID。 |
access_event_header 表包含每个请求的头信息,其组织形式如下:
字段 | 类型 | 描述 |
---|---|---|
event_id | int | 对应访问事件的数据库 ID。 |
header_key | varchar | 头部名称,例如 User-Agent。 |
header_value | varchar | 头部值,例如 Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0 |
经典的 DBAppender
的所有属性在 access
的 DBAppender
中都可用。后者提供了一个额外的选项,如下所述。
属性名称 | 类型 | 描述 |
---|---|---|
insertHeaders | boolean | 告诉 DBAppender 是否将所有传入请求的头信息填充到数据库中。 |
以下是使用 DBAppender
的示例配置。
示例:DBAppender
配置 (logback-examples/src/main/resources/chapters/appenders/conf/access/logback-DB.xml)
<configuration>
<appender name="DB" class="ch.qos.logback.access.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://localhost:3306/logbackdb</url>
<user>logback</user>
<password>logback</password>
</connectionSource>
<insertHeaders>true</insertHeaders>
</appender>
<appender-ref ref="DB" />
</configuration>
SiftingAppender
logback-access 中的 SiftingAppender
与其 logback-classic 对应项非常相似,主要区别在于 logback-access 中的默认判别器不是基于 MDC 的。正如其名称所示,AccessEventDiscriminator
使用 AccessEvent
中的指定字段作为选择嵌套 appender 的依据。如果指定字段的值为 null,则使用 defaultValue 属性中指定的值。
指定的 AccessEvent
字段可以是 COOKIE、REQUEST_ATTRIBUTE、SESSION_ATTRIBUTE、REMOTE_ADDRESS、LOCAL_PORT、REQUEST_URI 中的一个。请注意,前三个字段需要同时指定 AdditionalKey 属性。
以下是一个示例配置文件。
示例:SiftingAppender
配置 (logback-examples/src/main/resources/chapters/appenders/conf/sift/access-siftingFile.xml)
<configuration>
<appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender">
<Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator">
<Key>id</Key>
<FieldName>SESSION_ATTRIBUTE</FieldName>
<AdditionalKey>username</AdditionalKey>
<defaultValue>NA</defaultValue>
</Discriminator>
<sift>
<appender name="ch.qos.logback:logback-site:jar:1.3.8" class="ch.qos.logback.core.FileAppender">
<file>byUser/ch.qos.logback:logback-site:jar:1.3.8.log</file>
<layout class="ch.qos.logback.access.PatternLayout">
<pattern>%h %l %u %t \"%r\" %s %b</pattern>
</layout>
</appender>
</sift>
</appender>
<appender-ref ref="SIFTING" />
</configuration>
在上面的配置文件中,SiftingAppender
嵌套了 FileAppender
实例。键 "id" 被指定为一个变量,将在嵌套的 FileAppender
实例中可用。默认的判别器 AccessEventDiscriminator
将在每个 AccessEvent
中搜索名为 "username" 的会话属性。如果没有找到这样的属性,则使用默认值 "NA"。因此,假设名为 "username" 的会话属性包含每个登录用户的用户名,则每个用户在当前文件夹下的 byUser/ 文件夹中都会有一个以用户名称命名的日志文件,其中包含该用户的访问日志。