Logback 手册 - 第四章:Appenders - 2


来源:logback.qos.ch/manual/appe... 作者:Ceki Gülcü、Sébastien Pennec、Carl Harris 版权所有 © 2000-2022 QOS.ch Sarl

本文档使用 知识共享署名 - 非商业性使用 - 相同方式共享 2.5 许可协议 授权。


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() 方法或 LoggerContextstop() 方法来关闭 SocketAppender

远程服务器由 remoteHostport 属性标识。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 分发版本包括两个可用于接收 SocketAppenderSSLSocketAppender 日志事件的服务器选项。

  • ServerSocketReceiver 及其启用 SSL 的对应组件 SSLServerSocketReceiver,可以在应用程序的 logback.xml 配置文件中配置,以接收来自远程 socket appender 的事件。请参阅 Receivers 获取配置详细信息和使用示例。
  • SimpleSocketServer 及其启用 SSL 的对应组件 SimpleSSLSocketServer 均提供一个易于使用的独立 Java 应用程序,可从您的 shell 命令行界面进行配置和运行。这些应用程序简单地等待来自 SocketAppenderSSLSocketAppender 客户端的日志事件。每个接收到的事件都根据本地服务器策略进行记录。下面给出了使用示例。

使用 SimpleSocketServer

SimpleSocketServer 应用程序接受两个命令行参数:portconfigFile ;其中 port 是要监听的端口号,configFile 是以 XML 格式的配置脚本。

假设您在 logback-examples/ 目录中,使用以下命令启动 SimpleSocketServer

bash 复制代码
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
  src/main/java/chapters/appenders/socket/server1.xml

其中 6000 是要监听的端口号,server1.xml 是一个将 ConsoleAppenderRollingFileAppender 添加到根 logger 的配置脚本。启动 SimpleSocketServer 后,您可以使用 SocketAppender 从多个客户端向其发送日志事件。本手册附带的示例包括两个这样的客户端:chapters.appenders.SocketClient1chapters.appenders.SocketClient2。这两个客户端等待用户在控制台上输入一行文本。文本被封装为 debug 级别的日志事件,然后发送到远程服务器。这两个客户端在 SocketAppender 的配置上有所不同。SocketClient1 以编程方式配置 appender,而 SocketClient2 需要一个配置文件。

假设 SimpleSocketServer 正在本地主机上运行,您可以使用以下命令连接到它:

bash 复制代码
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)

传统方式

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 复制代码
<?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>

请注意,在上述配置脚本中,remoteHostportincludeCallerData 属性的值不是直接给出的,而是作为替换变量键。可以将这些变量的值指定为系统属性:

bash 复制代码
java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \
  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml

这个命令应该产生类似于之前 SocketClient1 示例的结果。

请允许我们重申一遍,日志事件的序列化并不具有侵入性。反序列化的事件携带与任何其他日志事件相同的信息。可以像对待本地生成的事件一样操作它;只是默认情况下序列化的日志事件不包括调用者数据。以下是一个例子来说明这一点。首先,使用以下命令启动 SimpleSocketServer

bash 复制代码
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
  src/main/java/chapters/appenders/socket/server2.xml

配置文件 server2.xml 创建了一个 ConsoleAppender,其布局输出调用者的文件名和行号以及其他信息。如果像以前那样使用配置文件 client1.xml 运行 SocketClient2,你会注意到服务器端的输出将包含两个问号在括号之间,而不是调用者的文件名和行号:

accesslog 复制代码
2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi

通过将 includeCallerData 选项设置为 true,可以简单地改变结果。使用以下命令会起到作用:

bash 复制代码
java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \
  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml

由于反序列化的事件可以像本地生成的事件一样处理,甚至可以将它们发送到第二个服务器进行进一步处理。作为练习,您可能希望设置两个服务器,其中第一个服务器将其从客户端接收到的事件隧道传输到第二个服务器。

使用 SimpleSSLSocketServer

SimpleSSLSocketServer 需要与 SimpleSocketServer 相同的 portconfigFile 命令行参数。此外,您还必须在命令行上提供日志服务器的 X.509 凭据的位置和密码,使用系统属性指定。

假设您位于 logback-examples/ 目录下,请使用以下命令启动 SimpleSSLSocketServer

bash 复制代码
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)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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>

请注意,与先前的示例一样,remoteHostport 的值使用替代变量键指定。另外,请注意 ssl 属性及其嵌套的 truststore 属性的存在,它使用替代变量指定了信任库的位置和密码。由于我们的示例服务器使用自签名证书,因此需要此配置。有关 SSLSocketAppender 的 SSL 配置属性的更多信息,请参阅 使用 SSL

我们可以通过将替代变量值作为系统属性在命令行上指定,使用此配置运行客户端应用程序:

bash 复制代码
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)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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 借助 DiscriminatorSMTPAppender 可以根据鉴别器返回的值将传入的事件分散到不同的缓冲区中。默认的鉴别器始终返回相同的值,以便所有事件都使用同一个缓冲区。 通过指定除默认鉴别器之外的鉴别器,可以接收包含特定用户、用户会话或客户端 IP 地址的事件的电子邮件消息。
evaluator IEvaluator 通过创建新的 <EventEvaluator/> 元素来声明此选项。用户希望用作 SMTPAppenderEvaluator 的类的名称需要通过 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.jaractivation.jar ,以便从 JNDI 中检索 Session
jndiLocation String jndiLocationjavax.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 APIJavaBeans Activation Framework。在尝试以下示例之前,请确保将这两个 jar 文件放置在类路径中。

一个示例应用程序 chapters.appenders.mail.EMail 生成一系列日志消息,然后是一个错误消息。它有两个参数。第一个参数是生成的日志事件数量的整数。第二个参数是 logback 配置文件。由 EMail 应用程序生成的最后一个日志事件(ERROR)将触发电子邮件消息的传输。

这是一个用于 Email 应用程序的示例配置文件:

示例:一个 SMTPAppender 配置文件示例(logback-examples/src/main/resources/chapters/appenders/mail/mail1.xml)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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 应用程序之前,您必须将 smtpHosttofrom 属性设置为适合您环境的值。在配置文件中设置正确的值后,执行以下命令:

bash 复制代码
java chapters.appenders.mail.EMail 100 src/main/java/chapters/appenders/mail/mail1.xml

您指定的收件人应该收到包含 100 个由 PatternLayout 格式化的日志事件的电子邮件消息。下图是 Mozilla Thunderbird 显示的结果电子邮件消息。

在下一个示例配置文件 mail2.xml 中,smtpHosttofrom 属性的值由变量替换确定。这是 mail2.xml 的相关部分。

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>

您可以通过命令行传递所需的参数:

bash 复制代码
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

请确保用适合您环境的值替换上述参数。

请注意,在这个最新的示例中,PatternLayoutHTMLLayout 替代,它将日志格式化为 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 版本

xml 复制代码
<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 复制代码
<?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 个事件返回 truelogback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java)

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 的核心功能,而让基类提供通用功能。

设置 SMTPAppenderEvaluator 选项将其指示使用自定义的评估器。下面的配置文件将 SMTPAppender 附加到根记录器。此 appender 使用一个 CounterBasedEvaluator 实例作为其事件评估器。

例子:使用自定义评估器和缓冲区大小的 SMTPAppender(logback-examples/src/main/resources/chapters/appenders/mail/mail3.xml)

传统

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>

规范格式 (1.3)

xml 复制代码
<?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 级别的。值得注意的语句包含一个标记。这是相关代码。

java 复制代码
Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN");
logger.error(notifyAdmin,
  "This is a serious error requiring the admin's attention",
   new Exception("Just testing"));

下一个配置文件仅在存在带有 NOTIFY_ADMINTRANSACTION_FAILURE 标记的事件时触发传出电子邮件。

例子:使用 OnMarkerEvaluatorSMTPAppender(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker.xml)

传统

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>

规范格式 (1.3)

xml 复制代码
<?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>

使用以下命令尝试一下:

bash 复制代码
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,但与先前的配置文件相同。

示例:带有 JaninoEventEvaluatorSMTPAppender(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker_Janino.xml)

传统风格

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 版本)

xml 复制代码
无法转换

身份验证/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)

传统风格

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 复制代码
<?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)

旧版配置

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 复制代码
<?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 值。

示例:带有 MDCBasedDiscriminatorSMTPAppender(logback-examples/src/main/resources/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml)

旧版配置

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 复制代码
<?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_eventlogging_event_propertylogging_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 计算。包含 MDCContext 属性的日志事件具有标志数为 1 。包含异常的日志事件具有标志数为 2 。同时包含两个元素的日志事件具有标志数为 3
caller_filename varchar 发出日志请求的文件的名称。
caller_class varchar 发出日志请求的类。
caller_method varchar 发出日志请求的方法的名称。
caller_line char 发出日志请求的行号。
event_id int 日志事件的数据库 ID。

logging_event_property 用于存储包含在 MDCContext 中的键和值。它包含以下字段:

字段 类型 描述
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 的实现,分别是 DataSourceConnectionSourceDriverManagerConnectionSourceJNDIConnectionSource

我们将首先介绍使用 DriverManagerConnectionSource 和 MySQL 数据库的配置示例。下面是一个配置文件示例。

示例:DBAppender 配置 (logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-driverManager.xml)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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:// 开头。

DriverManagerConnectionSourceConnectionSource 的一个实现,它根据连接 URL 以传统的 JDBC 方式获取连接。

请注意,该类将为每次调用 getConnection() 建立一个新的 Connection。建议您要么使用本地支持连接池的 JDBC 驱动程序,要么创建自己的 ConnectionSource 实现,以利用您已经使用的任何池化机制。如果您可以访问支持 javax.sql.DataSource 的 JNDI 实现(例如在 J2EE 应用服务器中),请参阅下面的 JNDIConnectionSource

使用 DataSource 连接到数据库相当类似。现在的配置使用了 DataSourceConnectionSource,它是一个基于 javax.sql.DataSource 的推荐 JDBC 方式来获取 ConnectionConnectionSource 的实现。

示例:DBAppender 配置 (logback-examples/src/main/resources/chapters/appenders/db/append-with-datasource.xml)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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 作为数据库。

xml 复制代码
<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)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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 的属性。

属性名称 类型 描述
syslogHost String syslog 服务器的主机名。
port String 要连接的 syslog 服务器上的端口号。通常情况下,不希望更改默认值 514
facility String facility 用于标识消息的来源。 facility 选项必须设置为以下字符串之一:KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT, ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7。大小写不重要。
suffixPattern String suffixPattern 选项指定发送到 syslog 服务器的消息中非标准化部分的格式。默认情况下,它的值为 [%thread] %logger %msg。任何 PatternLayout 可以使用的值都是正确的 suffixPattern 值。
stackTracePattern String stackTracePattern 属性允许自定义出现在每个堆栈跟踪行之前的字符串。此属性的默认值是 "\t",即制表符字符。任何被 PatternLayout 接受的值都是 stackTracePattern 的有效值。
throwableExcluded boolean throwableExcluded 设置为 true 将导致省略与 Throwable 相关联的堆栈跟踪数据。默认情况下,throwableExcluded 设置为 false,以便将堆栈跟踪数据发送到 syslog 服务器。

日志事件的 syslog 严重程度从日志事件的级别转换而来。DEBUG 级别转换为 7INFO 转换为 6WARN 转换为 4ERROR 转换为 3

由于 syslog 请求的格式遵循相当严格的规则,因此没有布局可与 SyslogAppender 一起使用。但是,使用 suffixPattern 选项可以让用户显示他想要的任何信息。

下面是使用 SyslogAppender 的示例配置。

示例:SyslogAppender 配置(logback-examples/src/main/resources/chapters/appenders/conf/logback-syslog.xml)

传统格式

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>

规范格式 (1.3)

xml 复制代码
<?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" 并记录一条消息。以下是关键代码:

java 复制代码
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)

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 将持续几秒钟以处理任何延迟到来的事件(如果有),然后关闭。

java 复制代码
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% 的话会有丢失== AsyncAppenderBlockingQueue 中缓冲事件。由 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 被停止时,AsyncAppenderstop 方法将等待工作线程完成的时间最长达到此超时时间。使用 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)

传统

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 复制代码
<?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)

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)

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_eventaccess_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 请求协议,如 HTTPHTTPS
method varchar 请求方法,通常为 GETPOST
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 的所有属性在 accessDBAppender 中都可用。后者提供了一个额外的选项,如下所述。

属性名称 类型 描述
insertHeaders boolean 告诉 DBAppender 是否将所有传入请求的头信息填充到数据库中。

以下是使用 DBAppender 的示例配置。

示例:DBAppender 配置 (logback-examples/src/main/resources/chapters/appenders/conf/access/logback-DB.xml)

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)

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/ 文件夹中都会有一个以用户名称命名的日志文件,其中包含该用户的访问日志。

版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:www.liujiajia.me/2023/12/6/l...

相关推荐
阿飞OnePiece8 个月前
springboot工程中使用slf4j+logback实现日志唯一编号
spring boot·apache log4j
洛小豆8 个月前
SpringBoot日志管理 —— 解决Logback生成 "LOG_PATH_IS_UNDEFINED" 文件夹问题
spring boot·后端·apache log4j
半夏之沫9 个月前
十年经验竟不懂Springboot日志
spring boot·spring·apache log4j
不识君9 个月前
动态调整Logback的Appender的解决方案
java·apache log4j
用户4266705916910 个月前
项目中如何配置和使用Log4j2.x
后端·apache log4j
hashcon10 个月前
【硬核】Log4j2 与 Logback 当初的选型以及在当前云原生环境下的反思与展望
java·spring·apache log4j
winjeg1 年前
logback VS log4j2 那些你注意不到的性能差距...
java·spring·apache log4j
乐乐家的乐乐1 年前
怎么说,手写一个日志框架
java·后端·apache log4j
佳佳_1 年前
Logback 手册 - 第八章:映射诊断上下文
apache log4j
佳佳_1 年前
Logback 手册 - 第六章:布局 - 2
apache log4j