将日志请求插入应用程序代码需要大量的计划和工作。观察表明,大约百分之四的代码专用于日志记录。因此,即使是中等大小的应用程序也将在其代码中嵌入数千个日志记录语句。给定它们的数量,我们需要工具来管理这些日志语句。
可以通过编程或使用以XML或Groovy格式表示的配置脚本来配置Logback。顺便说一句,现有的log4j用户可以使用我们的PropertiesTranslator Web应用程序将其log4j.properties文件转换为logback.xml。
logback初始化步骤如下:
Logback尝试在类路径中找到一个名为logback-test.xml的文件。
如果找不到这样的文件,则logback会尝试在类路径中找到一个名为logback.groovy的文件。
如果找不到这样的文件,它将在类路径中检查文件logback.xml。
如果找不到此类文件,则通过查找文件META-INF \ services \ ch,使用服务提供程序加载工具(在JDK 1.6中引入)来解析com.qos.logback.classic.spi.Configurator接口的实现。类路径中的qos.logback.classic.spi.Configurator。它的内容应指定所需的Configurator实现的完全限定的类名。
如果以上任何一项均未成功,则logback会使用BasicConfigurator自动进行自我配置,这会将日志输出定向到控制台。
最后一步是在没有配置文件的情况下尽力提供默认(但非常基本)的日志记录功能。
如果您使用的是Maven,并且将logback-test.xml放在src / test / resources文件夹下,则Maven将确保它不会包含在生成的工件中。因此,您可以在测试期间使用其他配置文件,即logback-test.xml,在生产环境中使用另一个文件,即logback.xml。
快速启动Joran解析给定的logback配置文件大约需要100毫秒。要减少应用程序启动时的时间,可以使用服务提供者加载工具(上面的项目4)加载您自己的自定义Configurator类,并以BasicConfigrator作为一个良好的起点。
一 基础配置
1.1 自动重新加载配置文件
修改后自动重新加载配置文件
如果指示这样做,logback-classic将扫描其配置文件中的更改,并在配置文件更改时自动重新配置自身。 为了指示经典的logback扫描其配置文件中的更改并自动重新配置自身,请将<configuration>
元素的scan属性设置为true,如下所示。
1 | <configuration scan="true"> |
默认情况下,将每分钟扫描一次配置文件是否有更改。 您可以通过设置<configuration>
元素的scanPeriod属性来指定其他扫描周期。 可以以毫秒,秒,分钟或小时为单位指定值。 这是一个例子:
1 | <configuration scan="true" scanPeriod="30 seconds" > |
如果未指定时间单位,则将时间单位假定为毫秒,这通常是不合适的。 如果更改默认扫描周期,请不要忘记指定时间单位。
在后台,当您将scan属性设置为true时,将安装ReconfigureOnChangeTask
。 该任务在单独的线程中运行,将检查您的配置文件是否已更改。 ReconfigureOnChangeTask
也会自动监视所有包含的文件。
由于在编辑配置文件时很容易出错,因此如果最新版本的配置文件具有XML语法错误,则它将回退到先前的配置文件,而没有XML语法错误。
1.2 动态修改日志级别
一般在生产环境上,日志的级别是INFO以上,但有时候程序出现问题(如SQL报错),少量日志不能尽快定位问题,这时候可以动态修改日志级别到DEBUG,打印更多日志后可以快速定位到问题。
1 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); |
Logback将执行日志事件输出的组件称为Appender,实现的Appender必须继承 ch.qos.logback.core.Appender
接口
此接口的主要方法总结如下:
1 | package ch.qos.logback.core; |
doAppende(E event) 方法的模板参数的真实类型根据logback module而定,在logback-classic中,E 为 ILoggingEvent 而在logback-access模块中,E 为 AcessEvent,doAppend可以说是logback框架最重要的部分,它负责将日志按照一定格式输出到指定设备。
Appender最终都会负责输出日志,但是他们也可能将日志格式化的工作交给Layout,或者Encoder对象。每一个Layout、Encoder只属于一个Appender。有些appender内置了固定的日志格式,所以并不需要layout/encoder声明。例如SockerAppender只是简单的序列化日志事件,然后就将它们通过网络传输。
二 Appender
AppenderBase
ch.qos.logback.core.AppenderBase
AppenderBase是一个继承自Appender接口的抽象类,它是所有Logback目前提供的appender的父类。虽然是个抽象类,但是实际上实现了doAppender()方法。
我们来看看它的doAppender方法的实现
1 | public synchronized void doAppend(E eventObject) { |
doAppend()
方法的此实现已同步。因此,从不同的线程登录到相同的追加程序是安全的。当一个线程(例如T)执行doAppend()
方法时,其他线程的后续调用将排队,直到T离开doAppend()
方法为止,以确保T对附加程序的独占访问。
由于这种同步并非总是适当的,因此Logback附带了ch.qos.logback.core.UnsynchronizedAppenderBase
,它与AppenderBase
类非常相似。
doAppend()
方法要做的第一件事是检查防护措施是否设置为true。如果是,它将立即退出。如果未设置防护,则在下一条语句中将其设置为true。防护措施确保doAppend()
方法不会递归调用自身。试想一下,一个在append()
方法之外的某个地方调用的组件想要记录一些东西。它的调用可以直接指向刚刚调用它的附加程序,从而导致无限循环和堆栈溢出。在下面的语句中,我们检查启动字段是否为true。如果不是,则
doAppend()
将发送警告消息并返回。换句话说,一旦关闭了附加器,就无法对其进行写入。 Appender对象实现LifeCycle接口,这意味着它们实现了start()
,stop()
和isStarted()
方法。设置附加程序的所有属性后,logback的配置框架Joran调用start()
方法来向附加程序发出信号以激活其属性。根据其种类,如果缺少某些属性或由于各种属性之间的干扰,追加程序可能无法启动。例如,假设文件创建依赖于截断模式,则FileAppender
在确定添加选项的值之前不能对其File选项的值起作用。显式激活步骤可确保在知道属性值后,附加程序对其属性进行操作。如果追加器无法启动或已停止,则将通过logback的内部状态管理系统发出警告消息。进行几次尝试后,为避免内部状态系统被相同警告消息的副本淹没,
doAppend()
方法将停止发出这些警告。下一个if语句检查附加过滤器的结果。根据过滤器链做出的决定,事件可以被拒绝或明确接受。如果过滤器链没有做出决定,则默认情况下会接受事件。
然后,
doAppend()
方法调用派生类对append()
方法的实现。此方法完成了将事件附加到适当设备的实际工作。最后,释放防护,以允许随后调用
doAppend()
方法。
在本手册的其余部分中,对于通过setter和getter方法使用JavaBeans自省功能动态推断出的任何属性,我们都保留术语“选项”或“属性”。
Logback-core
Logback-core为构建其他logback模块奠定了基础。 通常,logback-core中的组件需要一些(尽管最少)定制。 但是,在接下来的几节中,我们将介绍几个准备就绪即可使用的附加程序。
2.1 OutputStreamAppender
OutputStreamAppender将事件追加到java.io.OutputStream。 此类提供其他附加程序所基于的基本服务。 用户通常不直接实例化OutputStreamAppender对象,因为通常无法方便地将java.io.OutputStream类型映射到字符串,因为无法在配置脚本中指定目标OutputStream对象。 简而言之,您无法从配置文件配置OutputStreamAppender。 但是,这并不意味着OutputStreamAppender缺少可配置的属性。 接下来将描述这些属性。
Property Name | Type | Description |
---|---|---|
encoder | Encoder |
确定将事件写入基础“ OutputStreamAppender”的方式。 编码器在专用章节中进行了描述。 |
immediateFlush | boolean |
InstantFlush的默认值为“ true”。 立即刷新输出流可确保立即写出日志记录事件,并且在您的应用程序未正确关闭附加程序而退出的情况下也不会丢失。 另一方面,将此属性设置为’false’可能会使日志记录吞吐量增加四倍(您的里程可能会有所不同)。 同样,如果将InstantFlush设置为“ false”,并且在应用程序退出时未正确关闭附加程序,则尚未写入磁盘的日志记录事件可能会丢失。 |
OutputStreamAppender是其他三个附加程序的超类,即ConsoleAppender,FileAppender,而后者又是RollingFileAppender的超类。 下图说明了OutputStreamAppender及其子类的类图。
2.2 ConsoleAppender
如同它的名字一样,这个Appender将日志输出到console,更准确的说是System.out 或者System.err。
它包含的参数如下:
Property Name | Type | Description |
---|---|---|
encoder | Encoder |
参见OutputStreamAppender 属性。 |
target | String |
字符串值之一System.out或System.err。 默认目标是System.out。 |
withJansi | boolean |
默认情况下,withJansi属性设置为false。 将withJansi设置为true会激活Jansi库,该库在Windows计算机上提供对ANSI颜色代码的支持。 在Windows主机上,如果将此属性设置为true,则应在类路径上放置“ org.fusesource.jansi:jansi:1.17”。 请注意,默认情况下,基于Unix的操作系统(例如Linux和Mac OS X)支持ANSI颜色代码。 在Eclipse IDE下,您可能想尝试Eclipse Console插件中的ANSI。 |
看个简单的例子
1 | <configuration> |
2.3 FileAppender
将日志输出到文件当中,目标文件取决于file属性。是否追加输出,取决于append属性。
Property Name | Type | Description |
---|---|---|
append | boolean |
是否以追加方式输出。默认为true。 |
encoder | Encoder |
See OutputStreamAppender properties. |
file | String |
指定文件名。注意在windows当中,反斜杠 \ 需要转义,或直接使用 / 也可以。例如 c:/temp/test.logor 或 c:\temp\test.log 都可以。没有默认值,如果上层目录不存在,FileAppender会自动创建。 |
prudent | boolean |
是否工作在谨慎模式下。在谨慎模式下,FileAppender将会安全写入日志到指定文件,即时在不同的虚拟机jvm中有另一个相同的FileAppender实例。默认值:fales设置为true,意味着append会被自动设置成trueprudent依赖于文件排它锁。实验表明,使用文件锁,会增加3倍的日志写入消耗。比如说,当prudent模式为off,写入一条日志到文件只要10毫秒,但是prudent为真,则会接近30毫秒。prudent 模式实际上是将I/O请求序列化,因此在I/O数量较大,比如说100次/s或更多的时候,带来的延迟也会显而易见,所以应该避免。在networked file system(远程文件系统)中,这种消耗将会更大,可能导致死锁。 |
立即刷新默认情况下,每个日志事件都会立即刷新到基础输出流。 从某种意义上说,这种默认方法更安全,因为在您的应用程序未正确关闭附加程序的情况下退出时,不会丢失日志记录事件。 但是,为了显着提高日志记录吞吐量,您可能需要将InstantFlush属性设置为false。
以下是FileAppender的配置文件示例:
1 | <configuration> |
唯一命名的文件(按时间戳记)
在应用程序开发阶段或应用程序寿命短的情况下,例如 对于批处理应用程序,希望在每次启动新应用程序时创建一个新的日志文件。 借助于
示例:按时间戳唯一命名的FileAppender配置
1 | <configuration> |
timestamp元素具有两个必需属性key和datePattern以及一个可选的timeReference属性。 key属性是密钥的名称,在该名称下时间戳可作为变量提供给后续配置元素。 datePattern属性表示用于将当前时间(解析配置文件)转换为字符串的日期模式。日期模式应遵循SimpleDateFormat中定义的约定。 timeReference属性表示时间戳的时间参考。默认值为配置文件的解释/解析时间,即当前时间。但是,在某些情况下,使用上下文出生时间作为时间参考可能会很有用。这可以通过将timeReference属性设置为“ contextBirth”来完成。
要将记录器上下文的出生日期用作时间参考,您可以将timeReference属性设置为“ contextBirth”,如下所示。
1 | <configuration> |
2.4 RollingFileAppender
RollingFileAppender继承自FileAppender,提供日志目标文件自动切换的功能。例如可以用日期作为日志分割的条件。
RollingFileAppender有两个重要属性,RollingPolicy负责怎么切换日志,TriggeringPolicy负责何时切换。为了使RollingFileAppender起作用,这两个属性必须设置,但是如果RollingPolicy的实现类同样实现了TriggeringPolicy接口,则也可以只设置RollingPolicy这个属性。
Property Name | Type | Description |
---|---|---|
file | String |
See FileAppender properties. |
append | boolean |
See FileAppender properties. |
encoder | Encoder |
See OutputStreamAppender properties. |
rollingPolicy | RollingPolicy |
此选项是在发生翻转时将决定RollingFileAppender 行为的组件。 请参阅下面的更多信息。 |
triggeringPolicy | TriggeringPolicy |
这个选项是告诉“ RollingFileAppender”何时激活过渡程序的组件。 请参阅下面的更多信息。 |
prudent | boolean |
在谨慎模式下不支持FixedWindowRollingPolicy。 RollingFileAppender支持谨慎模式与TimeBasedRollingPolicy结合使用,尽管有两个限制。 在谨慎模式下,不支持也不允许文件压缩。 (我们不能让一个JVM写入文件,而另一个JVM对其进行压缩。) 无法设置FileAppender的file属性,并且必须将其保留为空白。 实际上,大多数操作系统不允许在打开另一个进程的同时重命名文件。 另请参阅FileAppender的属性。 |
2.4.1 RollingPolicy
RollingPolicy负责涉及文件移动和重命名的过渡过程。
RollingPolicy接口如下所示:
1 | package ch.qos.logback.core.rolling; |
过渡方法完成了归档当前日志文件所涉及的工作。 调用getActiveFileName()方法以计算当前日志文件的名称(将实时日志写入该文件)。 如getCompressionMode方法所指示,RollingPolicy还负责确定压缩模式。 最后,通过setParent方法为RollingPolicy提供了对其父级的引用。
2.4.2 TimeBasedRollingPolicy
TimeBasedRollingPolicy可能是最受欢迎的滚动策略。 它基于时间定义了过渡策略,例如按天或按月。 TimeBasedRollingPolicy承担了过渡以及触发所述过渡的责任。 实际上,TimeBasedTriggeringPolicy同时实现了RollingPolicy和TriggeringPolicy接口。
TimeBasedRollingPolicy的配置具有一个必需的fileNamePattern属性和几个可选属性。
Property Name | Type | Description |
---|---|---|
fileNamePattern | String |
参见下面的说明 |
maxHistory | int | 可选的maxHistory属性控制要保留的最大归档文件数,以异步方式删除较旧的文件。 例如,如果您指定每月滚动,并将maxHistory设置为6,则将保留6个月的归档文件,并删除6个月以上的文件。 请注意,由于删除了旧的归档日志文件,因此将适当删除为日志文件归档而创建的所有文件夹。 |
totalSizeCap | int | 可选的totalSizeCap属性控制所有存档文件的总大小。 当超过总大小上限时,最早的档案将被异步删除。 totalSizeCap属性还需要设置maxHistory属性。 此外,始终始终先应用“最大历史记录”限制,然后再应用“总大小上限”限制。 |
cleanHistoryOnStart | boolean | 如果设置为true,则将在追加程序启动时执行归档删除。 默认情况下,此属性设置为false。通常在过渡期间执行归档删除。 但是,某些应用程序的生存时间可能不足以触发翻转。 因此,对于这种短暂的应用程序,归档删除可能永远都没有执行的机会。 通过将cleanHistoryOnStart设置为true,将在附加程序启动时执行归档删除。 |
fileNamePattern
强制性的fileNamePattern属性定义了过渡(存档)日志文件的名称。它的值应包括文件名以及适当放置的%d转换说明符。 %d转换说明符可以包含java.text.SimpleDateFormat类指定的日期和时间模式。如果省略了日期和时间模式,则采用默认模式yyyy-MM-dd。根据fileNamePattern的值推断出过渡期。
请注意,可以设置或省略RollingFileAppender(TimeBasedRollingPolicy的父级)中的文件属性。通过设置包含FileAppender的file属性,可以将活动日志文件的位置与已归档日志文件的位置解耦。当前日志将始终以file属性指定的文件为目标。因此,当前活动的日志文件的名称不会随时间变化。但是,如果您选择忽略file属性,那么将根据fileNamePattern的值在每个期间重新计算活动文件。下面的示例应阐明这一点。
多个 %d 说明
可以指定多个%d说明符,但其中只有一个可以是主要的,即用于推断过渡期。 必须通过传递’aux’参数将所有其他标记标记为辅助标记(请参见下面的示例)。
多个%d指示符使您可以将存档文件组织为不同于过渡期的文件夹结构。 例如,下面显示的文件名模式按年和月组织日志文件夹,但每天午夜都将日志文件翻转。
1 | /var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log |
TimeZone
在某些情况下,您可能希望根据与主机不同的时区中的时钟来翻转日志文件。 可以在%d转换说明符中的日期和时间模式之后传递一个时区参数。 例如:
1 | aFolder/test.%d{yyyy-MM-dd-HH, UTC}.log |
如果指定的时区标识符未知或拼写错误,则将GMT时区视为由TimeZone.getTimeZone(String)方法规范指定。
fileNamePattern具有双重用途。首先,通过研究模式,logback计算所需的翻转周期。其次,它计算每个存档文件的名称。注意,两个不同的模式可以指定相同的周期性。模式yyyy-MM和yyyy @ MM都指定了月度过渡,尽管生成的存档文件将带有不同的名称。
通过设置文件属性,您可以将活动日志文件的位置与已归档日志文件的位置分离。日志记录输出将定向到file属性指定的文件中。因此,活动日志文件的名称不会随时间变化。但是,如果您选择忽略file属性,那么将根据fileNamePattern的值在每个期间重新计算活动文件。通过不设置文件选项,您可以避免文件重命名错误,该错误在翻转期间存在外部文件句柄引用日志文件时发生。
maxHistory属性控制要保留的存档文件的最大数量,从而删除较旧的文件。例如,如果您指定每月滚动,并将maxHistory设置为6,则将保留6个月的归档文件,并删除6个月以上的文件。请注意,由于删除了旧的归档日志文件,因此将适当删除为日志文件归档而创建的所有文件夹。
由于各种技术原因,翻转不是由时钟驱动的,而是取决于日志记录事件的到达。例如,在2002年3月8日,假设fileNamePattern设置为yyyy-MM-dd(每日翻转),则午夜之后第一个事件的到来将触发翻转。如果在午夜后的23分47秒内没有记录事件,则实际上将在3月9日凌晨00:23’47 AM而非0:00 AM发生翻转。因此,根据事件的到达率,可能会以一定的延迟触发翻转。但是,无论延迟如何,从某种意义上讲,已知过渡算法都是正确的,因为在特定时间段内生成的所有日志记录事件都将在界定该时间段的正确文件中输出。
这是RollingFileAppender与TimeBasedRollingPolicy结合使用的示例配置。
1 | <configuration> |
Logback Classic
虽然日志记录事件在logback核心中是通用的,但在logback classic中,它们总是ILoggingEvent的实例。管道处理只不过是一个典型的事件处理。
2.5 SocketAppender及SSLSocketAppender
到目前为止我们讲的appender都只能将日志输出到本地资源。与之相对的, SocketAppender就是被设计用来输出日志到远程实例中的。 SocketAppender输出日志采用明文方式, SSLSocketAppender则采用加密方式传输日志。
被序列化的日志事件的类型是 LoggingEventVO 继承ILoggingEvent接口。远程日志记录并非是侵入式的。在反序列化接收后,日志事件就可以好像在本地生成的日志一样处理了。多个SockerAppender可以向同一台日志服务器发送日志。SocketAppender并不需要关联一个Layout,因为它只是发送序列化的日志事件给远程日志服务器。SocketAppender的发送操作是基于TCP协议的。因此如果远程服务器是可到达的,则日志会被其处理,如果远程服务器宕机或不可到达,那么日志将会被丢弃。等到远程服务器复活,日志发送将会透明的重新开始。这种透明式的重连,是通过一个“连接“线程周期性的尝试连接远程服务器实现的。
Logging events会由TCP协议实现自动缓冲。这意味着,如果网络速度比日志请求产生速度快,则网络速度并不会影响应用。但如果网络速度过慢,则网络速度则会变成限制,在极端情况下,如果远程日志服务器不可到达,则会导致应用最终阻塞。不过,如果服务器可到达,但是服务器宕机了,这种情况,应用不会阻塞,而只是丢失一些日志事件而已。
需要注意的是,即使SocketAppender没有被logger链接,它也不会被gc回收,因为他在connector thread中任然存在引用。一个connector thread 只有在网络不可达的情况下,才会退出。为了防止这个垃圾回收的问题,我们应该显示声明关闭SocketAppender。长久存活并创建/销毁大量的SocketAppender实例的应用,更应该注意这个问题。不过大多数应用可以忽略这个问题。如果JVM在SocketAppender关闭之前将其退出,又或者是被垃圾回收,这样子可能导致丢失一些还未被传输,在管道中等待的日志数据。为了防止避免日志丢失,经常可靠的办法就是调用SocketAppender的close方法,或者调用LoggerContext的stop方法,在退出应用之前。
远程服务器由远程主机和端口属性标识。下表列出了SocketAppender属性。SSLSocketAppender支持许多附加的配置属性,在名为usingssl的部分中有详细说明。
下面我们来看看SocketAppender的属性:
Property Name | Type | Description |
---|---|---|
includeCallerData | boolean |
includeCallerData选项采用布尔值。 如果为true,则呼叫者数据将对远程主机可用。 默认情况下,没有主叫方数据发送到服务器。 |
port | int |
远程服务器的端口号。 |
reconnectionDelay | Duration |
reconnectionDelay选项采用一个持续时间字符串,例如“ 10秒”,表示在每次尝试与服务器的失败连接之间等待的时间。 此选项的默认值为30秒。 将此选项设置为零将关闭重新连接功能。 请注意,如果成功连接到服务器,将不存在连接器线程。 |
queueSize | int |
queueSize属性采用一个整数(大于零),该整数表示要保留以传递给远程接收器的日志事件的数量。 当队列大小为1时,到远程接收器的事件传递是同步的。 当队列大小大于1时,假定队列中有可用空间,新事件将进入队列。 使用大于1的队列长度可以消除由瞬时网络延迟引起的延迟,从而提高性能。另请参见eventDelayLimit属性。 |
eventDelayLimit | Duration |
eventDelayLimit选项采用持续时间字符串,例如“ 10秒”。 它表示在本地队列已满(即已经包含queueSize事件)的情况下,丢弃事件之前要等待的时间。 如果远程主机持续缓慢地接受事件,则可能会发生这种情况。 此选项的默认值为100毫秒。 |
remoteHost | String |
服务器的主机名。 |
ssl | SSLConfiguration |
该属性仅受SSLSocketAppender支持,提供使用附加程序将使用的SSL配置,如使用SSL中所述。 |
标准的Logback Classic包含四个可供使用的Receiver用来接收来自SocketAppender的logging evnets。
- ServerSocketReceiver,以及允许SSL的副本SSLSeverSocketReceiver。详细配置查看 Receivers
- SimpleSocketServer,以及允许SSL的副本SimpleSSLSocketServer。
SimpleSocketServer应用程序采用两个命令行参数:port和configFile; 其中port是要侦听的端口,configFile是XML格式的配置脚本。
假设您位于logback-examples /目录中,请使用以下命令启动SimpleSocketServer:
1 | java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ |
其中6000是要侦听的端口号,server1.xml是一个配置脚本,它将ConsoleAppender和RollingFileAppender添加到根记录器。 启动SimpleSocketServer之后,可以使用SocketAppender从多个客户端向其发送日志事件。 与本手册相关的示例包括两个这样的客户端chapters.appenders.SocketClient1
和hapters.appenders.SocketClient2
这两个客户端都等待用户在控制台上键入一行文本。 文本封装在级别为debug的日志事件中,然后发送到远程服务器。 这两个客户端的SocketAppender配置不同。 SocketClient1以编程方式配置附加程序,而SocketClient2需要配置文件。
假设SimpleSocketServer在本地主机上运行,请使用以下命令连接到它:
1 | java chapters.appenders.socket.SocketClient1 localhost 6000 |
您键入的每一行都应出现在上一步中启动的SimpleSocketServer的控制台上。 如果停止并重新启动SimpleSocketServer,则客户端将透明地重新连接到新的服务器实例,尽管断开时生成的事件将简单地(并且不可撤消地)丢失。
与SocketClient1不同,示例应用程序SocketClient2不会自行配置回传。 它需要XML格式的配置文件。 下面显示的配置文件client1.xml创建一个SocketAppender并将其附加到根记录器。
1 | <configuration> |
2.6 ServerSocketAppender及SSLSeverSocketAppender
前面讨论的SocketAppender组件(及其支持SSL的组件)旨在允许应用程序通过网络连接到远程日志记录服务器,以便将日志记录事件传递到服务器。在某些情况下,让应用程序启动与远程日志记录服务器的连接可能不方便或不可行。对于这些情况,Logback提供了ServerSocketAppender。
ServerSocketAppender无需侦听TCP套接字,而无需启动与远程日志记录服务器的连接,而是被动地侦听TCP套接字,以等待来自远程客户端的传入连接。传递给附加程序的日志事件将分发给每个连接的客户端。未连接任何客户端时发生的日志记录事件将被丢弃。
除了基本的ServerSocketAppender之外,Logback还提供SSLServerSocketAppender,它使用安全的加密通道将日志记录事件分发给每个连接的客户端。此外,启用SSL的附加程序完全支持基于相互证书的身份验证,可用于确保只有授权的客户端才能连接到附加程序以接收日志事件。
编码日志事件以在线上传输的方法与SocketAppender所使用的方法相同。每个事件都是ILoggingEvent的序列化实例。仅连接启动方向相反。在建立与日志服务器的连接时,SocketAppender充当主动对等方,而ServerSocketAppender是被动的;它侦听来自客户端的传入连接。
ServerSocketAppender子类型只能与Logback接收器组件一起使用。有关此组件类型的其他信息,请参见接收器。
ServerSocketAppender支持以下配置属性:
Property Name | Type | Description |
---|---|---|
address | String |
追加程序将在其上侦听的本地网络接口地址。 如果未指定此属性,则附加程序将在所有网络接口上侦听。 |
includeCallerData | boolean |
如果为true,则呼叫者数据将对远程主机可用。 默认情况下,没有呼叫者数据发送到客户端。 |
port | int |
追加程序将侦听的端口号。 |
ssl | SSLConfiguration |
该属性仅受SSLServerSocketAppender支持,提供使用附加程序将使用的SSL配置,如使用SSL中所述。 |
以下示例说明了使用ServerSocketAppender的配置:
1 | <configuration debug="true"> |
2.7 DBAppender
DBAppender以独立于Java编程语言的格式将日志记录事件插入到三个数据库表中。
这三个表是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 | tested version(s) | tested JDBC driver version(s) | supports getGeneratedKeys() method |
is a dialect provided by logback |
---|---|---|---|---|
DB2 | untested | untested | unknown | NO |
H2 | 1.2.132 | - | unknown | YES |
HSQL | 1.8.0.7 | - | NO | YES |
Microsoft SQL Server | 2005 | 2.0.1008.2 (sqljdbc.jar) | YES | YES |
MySQL | 5.0.22 | 5.0.8 (mysql-connector.jar) | YES | YES |
PostgreSQL | 8.x | 8.4-701.jdbc4 | NO | YES |
Oracle | 10g | 10.2.0.1 (ojdbc14.jar) | YES | YES |
SQLLite | 3.7.4 | - | unknown | YES |
Sybase SQLAnywhere | 10.0.1 | - | unknown | YES |
实验表明,在“标准” PC上,将单个事件写入数据库大约需要10毫秒。 如果使用池连接,则该数字下降到大约1毫秒。 请注意,大多数JDBC驱动程序已经附带连接池支持。
可以通过几种不同的方式来配置登录回以使用DBAppender,这取决于一种必须连接到数据库的工具以及数据库本身。 配置DBAppender的关键问题在于设置其ConnectionSource对象,我们将很快发现。
为数据库配置DBAppender后,日志记录事件将发送到指定的数据库。 如前所述,logback使用三个表来存储日志记录事件数据。
logging_event表包含以下字段:
Field | Type | Description |
---|---|---|
timestamp | big int |
创建日志事件时有效的时间戳。 |
formatted_message | text |
使用org.slf4j.impl.MessageFormatter进行格式化后,添加到日志记录事件中的消息,以防对象随消息一起传递。 |
logger_name | varchar |
用于发出记录请求的记录器的名称。 |
level_string | varchar |
日志记录事件的级别。 |
reference_flag | smallint |
Logback使用此字段来标识具有异常或MDCproperty值关联的日志记录事件。它的值是由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 |
日志记录事件的数据库标识。 |
logging_event_property用于存储MDC或上下文中包含的键和值。 它包含以下字段:
Field | Type | Description |
---|---|---|
event_id | int |
日志记录事件的数据库标识。 |
mapped_key | varchar |
The key of the MDC property |
mapped_value | text |
The value of the MDC property |
logging_event_exception表包含以下字段
Field | Type | Description |
---|---|---|
event_id | int |
The database id of the logging event. |
i | smallint |
The index of the line in the full stack trace. |
trace_line | varchar |
The corresponding line |
2.7.1 ConnectionSource
ConnectionSource接口提供了一种可插入的方法,可以透明地为需要使用java.sql.Connection的登录类获取JDBC连接。 当前有ConnectionSource的三种实现,即DataSourceConnectionSource,DriverManagerConnectionSource和JNDIConnectionSource。
我们将审查的第一个示例是使用DriverManagerConnectionSource和MySQL数据库的配置。 以下配置文件是需要的。
1 | <configuration> |
必须声明正确的驱动程序。在这里,使用com.mysql.jdbc.Driver类。网址必须以jdbc:mysql://开头。
DriverManagerConnectionSource是ConnectionSource的实现,该实现基于连接URL以传统的JDBC方式获取连接。
请注意,此类将为每次对getConnection()的调用建立一个新的Connection。建议您使用本机支持连接池的JDBC驱动程序,或者创建自己的ConnectionSource实现,以利用您已经在使用的任何池机制。如果您有权访问支持javax.sql.DataSource的JNDI实现,例如在J2EE应用程序服务器中,请参阅下面的JNDIConnectionSource。
使用数据源连接到数据库非常相似。现在,该配置使用DataSourceConnectionSource,它是ConnectionSource的实现,该实现基于javax.sql.DataSource以推荐的JDBC方式获取Connection。
1 | <configuration debug="true"> |
请注意,在此配置示例中,我们大量使用了替换变量。 当必须将连接详细信息集中在单个配置文件中并由Logback和其他框架共享时,它们有时很方便。
2.7.2 JNDIConnectionSource
JNDIConnectionSource是Logback中附带的另一个ConnectionSource实现。 顾名思义,它从JNDI检索javax.sql.DataSource,然后利用它来获取java.sql.Connection实例。 假设应用服务器支持javax.sql.DataSource的远程访问,则JNDIConnectionSource主要设计用于在J2EE应用服务器内部或由应用服务器客户端使用。 因此,可以利用连接池和应用程序服务器提供的其他任何优点。 更重要的是,您的应用程序将变得更加干燥,因为不再需要在logback.xml中定义数据源。
例如,这是Tomcat的配置代码段。 尽管所有受支持的数据库系统(上面列出)都可以使用,但它假定PostgreSQL为数据库。
1 | <Context docBase="/path/to/app.war" path="/myapp"> |
在J2EE服务器中定义了DataSource之后,您的logback配置文件可以轻松地引用它,如下面的示例所示。
1 | <configuration debug="true"> |
请注意,此类将使用无参数构造函数获取javax.naming.InitialContext。 在J2EE环境中执行时,通常可以正常工作。 在J2EE环境之外时,请确保提供JNDI提供程序的文档所述的jndi.properties文件。
2.8 SyslogAppender
syslog协议是一个非常简单的协议:syslog发送方向syslog接收方发送一条小消息。 接收器通常称为syslog守护程序或syslog服务器。 Logback可以将消息发送到远程syslog守护程序。 这是通过使用SyslogAppender实现的。
这是您可以传递给SyslogAppender的属性。
Property Name | Type | Description |
---|---|---|
syslogHost | String |
syslog服务器的主机名。 |
port | String |
syslog服务器上要连接的端口号。 通常,人们不希望更改默认值514。 |
facility | String |
该功能旨在识别消息的来源。设施选项必须设置为以下字符串之一: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服务器。 |
日志事件的系统日志严重性是从日志事件的级别转换而来的。 DEBUG级别转换为7,INFO转换为6,WARN转换为4,ERROR转换为3。
由于syslog请求的格式遵循相当严格的规则,因此SyslogAppender不能使用任何布局。 但是,使用suffixPattern选项可以使用户显示她希望的任何信息。
这是使用SyslogAppender的示例配置。
1 | <configuration> |
测试此配置时,应验证远程syslog守护程序是否接受来自外部源的请求。 经验表明,默认情况下,syslog守护程序通常会拒绝通过网络连接发出的请求。
2.9 AsyncAppender
AsyncAppender异步记录ILoggingEvents。它仅充当事件分发程序,因此必须引用另一个追加程序以执行任何有用的操作。
如果80%FULL AsyncAppender将事件缓冲在BlockingQueue中,则默认丢失。由AsyncAppender创建的辅助线程从队列的开头获取事件,并将其分派到附加到AsyncAppender的单个追加程序。请注意,默认情况下,如果AsyncAppender的队列已满80%,它将删除级别TRACE,DEBUG和INFO的事件。该策略以事件丢失为代价,对性能产生了惊人的有利影响。
应用程序停止/重新部署在应用程序关闭或重新部署后,必须停止AsyncAppender才能停止并回收工作线程,并从队列中清除日志记录事件。这可以通过停止LoggerContext来完成,该LoggerContext将关闭所有附加程序,包括任何AsyncAppender实例。 AsyncAppender将等待工作线程刷新到maxFlushTime中指定的超时。如果发现在LoggerContext关闭期间已丢弃排队的事件,则可能需要增加超时时间。将maxFlushTime指定为0值将强制AsyncAppender在从stop方法返回之前等待刷新所有排队的事件。
POST SHUTDOWN CLEANUP根据JVM关闭的模式,处理排队事件的辅助线程可能会中断,从而使事件滞留在队列中。当LoggerContext没有干净停止或JVM在典型控制流之外终止时,通常会发生这种情况。为了避免在这种情况下中断工作线程,可以在JVM运行时中插入一个关闭挂钩,以在启动JVM关闭后正确停止LoggerContext。当其他关闭挂钩尝试记录事件时,关闭挂钩也可能是干净关闭Logback的首选方法。
这是AsyncAppender允许的属性列表:
Property Name | Type | Description |
---|---|---|
queueSize | int |
阻塞队列的最大容量。 默认情况下,queueSize设置为256。 |
discardingThreshold | int |
默认情况下,当阻塞队列剩余容量为20%时,它将丢弃级别TRACE,DEBUG和INFO的事件,仅保留级别WARN和ERROR的事件。 要保留所有事件,请将discardingThreshold设置为0。 |
includeCallerData | boolean |
提取呼叫者数据可能会非常昂贵。 为了提高性能,默认情况下,将事件添加到事件队列时,不会提取与事件关联的呼叫者数据。 默认情况下,仅复制“廉价”数据(例如线程名和MDC)。 您可以通过将includeCallerData属性设置为true来指示此附加程序包括调用方数据。 |
maxFlushTime | int |
根据队列深度和到引用的附加程序的延迟,AsyncAppender可能花费不可接受的时间完全刷新队列。 当LoggerContext停止时,AsyncAppender的stop方法将等待此超时,以使工作线程完成。 使用maxFlushTime指定最大队列刷新超时(以毫秒为单位)。 在此窗口内无法处理的事件将被丢弃。 此值的语义与Thread.join(long)的相同。 |
neverBlock | boolean |
如果为false(默认值),则附加程序将在附加到完整队列时阻塞,而不是丢失消息。 设置为true,附加程序将仅丢弃该消息,并且不会阻止您的应用程序。 |
默认情况下,事件队列配置的最大容量为256个事件。如果队列已满,则将阻止应用程序线程记录新事件,直到工作线程有机会分派一个或多个事件。当队列不再达到最大容量时,应用程序线程能够再次开始记录事件。因此,当追加程序以其事件缓冲区的容量或接近其事件缓冲区的容量进行操作时,异步日志记录将变为伪同步。这不一定是一件坏事。附加程序旨在允许应用程序继续运行,尽管花费一些时间记录事件,直到减轻附加程序缓冲区的压力。
为了最大程度地提高应用程序吞吐量,最佳地调整appender事件队列的大小取决于几个因素。以下任何一个或所有因素都可能导致显示伪同步行为:
- 大量的应用程序线程
- 每个应用程序调用都会记录大量日志事件
- 每个日志记录事件有大量数据
- 子追加器的高延迟
- 为了保持运行状态,增加队列的大小通常会有所帮助,但会浪费应用程序可用的堆空间。
混乱的行为根据上面的讨论,并且为了减少阻塞,默认情况下,当剩余的队列容量不到20%时,AsyncAppender将丢弃级别TRACE,DEBUG和INFO的事件,仅保留级别WARN和ERROR的事件。当队列的容量不足20%时,此策略可确保以级别TRACE,DEBUG和INFO的成本丢失事件来无阻塞地处理日志事件(因此具有出色的性能)。通过将discardingThreshold属性设置为0(零),可以防止事件丢失。
1 | <configuration> |
三 自定义Appender
您可以通过对AppenderBase进行子类化来轻松编写您的附加程序。 它处理对大多数附加程序共享的过滤器,状态消息和其他功能的支持。 派生类仅需要实现一种方法,即append(Object eventObject)。
接下来列出的CountingConsoleAppender在控制台上附加了有限数量的传入事件。 达到限制后它将关闭。 它使用PatternLayoutEncoder格式化事件并接受名为limit的参数。 因此,除了append(Object eventObject),还需要其他一些方法。 如下所示,这些参数是由logback的各种配置机制自动处理的。
1 | package chapters.appenders; |
start()方法检查是否存在PatternLayoutEncoder。 如果未设置编码器,则附加器无法启动并发出错误消息。
此自定义附加程序说明了两点:
- 遵循setter / getter JavaBeans约定的所有属性均由logback配置器透明处理。 start()方法(在登录配置期间会自动调用)负责验证是否设置了附加程序的各种属性并使其一致。
- AppenderBase.doAppend()方法调用其派生类的append()方法。 实际的输出操作发生在append()方法中。 特别是,在此方法中,追加程序通过调用事件的布局来格式化事件。
四 配置文件示例
logback-spring.xml
1 |
|
对于springboot项目,部分配置也可以通过配置文件快速生成
1 | # 日志根配置 |