1. 概述
OutputStreamAppender是logback中自带的一个appender,主要是用于处理outputstream的event。该类提供了一些appender的基本方法实现,继承于OutputStreamAppender的子appender可以依赖使用。一般并不会直接初始化OutputStreamAppender对象,因为OutputStream并不能很方便的映射成字符串,因此就无法在配置中指定。简单的来说,就是不能在配置文件中显示指定OutputStreamAppender 。但是并不意味着OutputStreamAppender 没有配置属性。支持的属性如下
- encoder:Encoder类型,决定了event写入到OutputStream的方式。
- immediateFlush:boolean类型,默认值是true。这个属性决定了event 是否会立刻输出,从而避免应用程序在非正常情况下退出导致event丢失。而且,如果把该值设置为false,那么event的吞吐量必然会加大,而且在非正常停止程序的时候,还没有写出的event将会全部丢失。所以建议设置为true。当然有需要大吞吐量的场景,也可以设置为false
OutputStreamAppender 的类图如下:
可以看到,有三个子类,ConsoleAppender, FileAppender和RollingFileAppender。而RollingFileAppender又是FileAppender的子类
接下来逐个介绍
appender | 特点 |
---|---|
ConsoleAppender | 支持日志输出到控制台 |
FileAppender | 支持日志输出到文件,可以在文件名使用时间表示 |
RollingFileAppender | 支持日志滚动,通过policy和trigger完成什么时候,做什么的日志滚动自定义 |
2. ConsoleAppender
2.1 介绍
ConsoleAppender的最终输出是在控制台,或者更加准确的来说是system.out或者是system.err,默认是system.out。event的输出格式是由指定的encoder进行格式化的,这个配置项在前面提到过。system.out和system.err都是java.io.PrintStream类型的,因此,在进行IO操作的时候都是封装成OutputStreamWriter
2.2 配置项
- encoder:同上
- target:string类型,可以指定system.out或者是system.err,默认是out
- withJansi:Boolean类型,默认是设置为false。意思是是否开启使用jansi类库。如果开启的话,那么就可以在Windows系统中支持ANSI颜色编码。
在Windows中,如果该配置被设置为true,那么就需要在classpath中增加org.fusesource.jansi:jansi:${jansi.version}。
而在linux和Unix中,则不需要配置classpath,因为是默认支持ANSI颜色编码的。
2.3 源代码
2.4 使用配置示例
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
可以看到上面的例子中我们定义了一个名为STDOUT的ConsoleAppender,在encoder中指定了控制台输出的格式。
java 代码
package com;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class test {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(test.class);
logger.info("this is a test log");
}
}
结果输出:
3. FileAppender
3.1 介绍
FileAppender作为OutputStreamAppender的子类,主要是将event输出到文件中。
目标文件可以在配置项file中指定。如果文件已经存在,那么就会根据配置项append的值来决定是truncate(截断)还是append(追加)
3.2 配置项
- append:Boolean类型。默认为true,如果值为true,那么当文件已经存在的时候,会在当前文件上追加写入event。如果值为false,那么会从文件的0索引位置写入event,即覆盖之前的写入内容。
- encoder:同上
- file:String类型。用来指定目标输出文件,如果文件不存在,会自动创建,在MS系统中,注意不要忘记反斜杠。例如值c:\temp\test。可能无法正确解读,因为“\t”是解释为单个制表符(\u0009)的转义序列。正确的值可以指定为c:/temp/test。或替换为c:\temp\test。
该选项没有默认值 - prudent:Boolean类型。如果值为true,那么即使有运行在不同的JVM(运行在不同的主机)中其他的FileAppender实例,也可以保证将event安全的输出到文件中。
默认值为false
注:值为true则意味着append配置项默认被设置为true
prudent 实现逻辑:(很耗费性能,谨慎使用)
1)上面说的不同实例安全写入同一文件,主要是依赖于文件的排它锁。官方数据显示,使用排它锁的成本大约是不使用的3倍。在一台PC上,写入一个本地硬盘的文件,如果prudent关闭的话写入一个event要花10毫秒的时间,如果开启的话,则要花费30毫秒的时间。即关闭该模式时候,每秒可以输出100 000 个event,但是开启的时候,就只能输出33 000个。
2)prudent模式可以有效的序列化写入同一文件的所有JVM的IO操作。因此,当JVM的数据增加的时候,那么必然会导致IO序列化的延迟。 只要每秒IO操作总数不超过20个,那么该影响就可以忽略不计。如果超过100那么就必须要考虑对性能的影响,建议关闭该模式
3)当日志文件是在网络系统中的时候,那么消耗的成本将会更大。还有需要注意的是,网络系统上的文件锁会有一些偏差即当一个线程释放掉锁以后,马上又获取了锁,那么这个时候其他在等待的线程就会处于死锁的状态。
网络速度和操作系统的实现逻辑都对该模式有很大的影响。logback提供了FileLockSimulator ,可以模拟特定环境下的prudent模式
3.3 源代码
3.4 使用配置示例
- 普通输出
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>
<!-- set immediateFlush to false for much higher logging throughput -->
<immediateFlush>true</immediateFlush>
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
如上面的例子中,我们定义了一个名为FILE的FileAppender,其中file属性设置为testFile.log,即接下来的所有日志输出都会在这个文件中。immediateFlush设置为true,即每一条日制都会实时刷新到testFile.log中。encoder中定义了输出的格式
Java代码:
package com;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class test {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(test.class);
logger.info("this is a test log");
}
}
执行之后客户端输出:
因为设置了level为debug,所以我们可以看到一些框架的输出。像上面的截图中,有体现一些配置项的设置值。
然后我们也会发现在执行目录下多了一个文件,文件内容为
- 带时间戳输出
下面的例子会使用到一个timestamp的配置项
timestamp 有两个必需属性key和datePattern,还有一个可选的timeReference 。
1). key: 当该元素被其他元素作为变量引用的时候,那么key就是变量名
2). datePattern:将当前时间以何种格式转化为字符串,需要遵守SimpleDateFormat中的约定
3). timeReference :对需要转换的时间戳的时间参考。默认是配置文件的解析时间,即当前时间。然而,在某些情况,可能会想要系统启动的上下文时间,那么就可以通过将timeReference 设置为contextBirth实现
配置文件
<configuration>
<!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
the key "bySecond" into the logger context. This value will be
available to all subsequent configuration elements. -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- use the previously created timestamp to create a uniquely
named log file -->
<file>log-${bySecond}.txt</file>
<encoder>
<pattern>%logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
如上面的例子中,先定义了一个名为bySecond的timestamp,其格式为yyyyMMdd’T’HHmmss。在定义名为FILE的FileAppender的时候,其中file属性设置为log-${bySecond}.txt,即接下来的所有日志输出都会在这个带有指定格式的时间文件中中。encoder中定义了输出的格式
Java代码
package com;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class test {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(test.class);
logger.info("this is a test log");
}
}
执行之后,我们发现在执行目录下,出现了一个新的日志文件
可以看出,文件名的格式就是上面配置文件中指定的,log前缀加上一个日期时间到秒的txt文件
文件内容为
4. RollingFileAppender
4.1 介绍
继承于FileAppender 并且拥有日志文件滚动的能力。例如,RollingFileAppender会将日志输出到一个指定文件中,等到触发某种条件的时候,就会转向输出到另外一个文件
RollingFileAppender的实现,主要依赖两个非常重要的子组件
- RollingPolicy:主要负责执行流转所需要的操作
- TriggeringPolicy:主要负责流转的触达条件
这两个子组件,RollingPolicy决定了做什么,TriggeringPolicy决定了什么时候。
两个组件都是必须要配置的 。但是如果RollingPolicy 实现了TriggeringPolicy 接口的话,那么就只需要指定RollingPolicy 就可以了
4.2 配置项
- file:输出的文件名
- append:是否追加
- encoder:同上
- rollingPolicy:流转策略
- triggeringPolicy:触发策略
- prudent:在该模式下是不支持FixedWindowRollingPolicy 的
但是可以使用TimeBasedRollingPolicy ,不过有两个限制:
1. 不支持文件压缩,因为不能一个JVM在写文件,一个JVM在压缩文件
2. 不能设置file配置项,必须保留空。的确,在大部分的文件系统中都不可以在打开文件的时候更改文件名称
4.3 源码
4.4 policy
可以看到,在RollingPolicy接口中有4个方法需要实现:
- rollover():主要是完成存档当前文件的工作,流转日志文件的主要实现逻辑
- getActiveFileName():当有线程要写入event的时候返回文件名
- getCompressionMode():返回压缩模式
- setParent(FileAppender<?> appender):设置当前policy需要关注的一些appender
4.4.1 TimeBasedRollingPolicy
一:介绍
- TimeBasedRollingPolicy是使用频率最高的policy。
- 定义了一种可以根据时间滚动的policy,例如月,天。
- 同时实现了policy和trigger的工作
二:源代码
三:支持的配置项
支持的配置项
- fileNamePattern:https://blog.csdn.net/javadocdoc/article/details/123342235
- TimeZone
在特定的环境下,我们根据非主机的时区来进行时间判断完成日志的滚动。可以在%d转换符中日期时间格式的后面跟着时区参数
例如
aFolder/test.%d{yyyy-MM-dd-HH, UTC}.log
如果没有指定时区,或者是指定了一个错误的时区,那么默认是 GMT时区 - maxHistory
- int类型。
- 可选。
- 表示归档文件可以存储的最大个数,如果当前归档文件个数超过了设置值,那么会异步删除一些最早的文件,直到满足该条件。例如,如果设置的是按月流转,该值设置的是6,那么就只会保留近6个月的文件,6个月之前的文件都会被删除。而且如果是当初因为生成日志而创建的文件,也会被删除
- totalSizeCap
- int类型。
- 可选。
- 表示归档文件的大小,如果当前归档文件大小超过了设置值,那么会异步删除一些最早的文件,直到满足该条件。
- totalSizeCap使用的时候需要也设置一下maxHistory的值,而且需要首先满足maxHistory的条件,其次满足totalSizeCap
- cleanHistoryOnStart
- Boolean类型。
- 如果设置为true,那么归档文件会在appender启动的时候删除。
- 默认是false。
设置为true的使用场景,归档文件一般是在日志滚动的过程中删除。但是对于一些执行时间短的应用程序,可以没有到触发滚动的时候就已经执行结束了,那么就永远无法实现日志的清除。所以就需要该值设置为true,在appender启动的时候,就将归档文件删除掉
四:使用实例
普通配置
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
如上面的配置文件:
- 定义了一个名为FILE的RollingFileAppender。
- 指定了file配置项的值为logFile.log,即日志的实时输出文件为logFile.log
- 定义了rollingPolicy为TimeBasedRollingPolicy,即基于时间来触发日志流转的策略
- fileNamePattern为logFile.%d{yyyy-MM-dd}.log,等价于logFile.%d.log。即归档日志文件的文件名为logFile.加日期.log
- maxHistory为30,即指保留近30天的日志
- totalSizeCap为3GB,即归档日志文件最多只能保留3GB(maxHistory比totalSizeCap优先级高)
- encoder,指定了日志输出的格式
- 设置root的日志级别为DEBUG,使用FILE的appender进行输出
prudent配置
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- Support multiple-JVM writing to the same log file -->
<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} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
如上面的配置文件:
- 定义了一个名为FILE的RollingFileAppender。
- 指定了prudent配置项的值为true,即使用谨慎模式,日志实时输出,避免丢失
- 定义了rollingPolicy为TimeBasedRollingPolicy,即基于时间来触发日志流转的策略
- fileNamePattern为logFile.%d{yyyy-MM-dd}.log,等价于logFile.%d.log。即归档日志文件的文件名为logFile.加日期.log
- maxHistory为30,即指保留近30天的日志
- totalSizeCap为3GB,即归档日志文件最多只能保留3GB(maxHistory比totalSizeCap优先级高)
- encoder,指定了日志输出的格式
- 设置root的日志级别为DEBUG,使用FILE的appender进行输出
4.4.2 SizeAndTimeBasedRollingPolicy
一:介绍
由日期和日志大小来共同触发日志文件的流转,注意,这里的日志大小指的是实时写入的生效文件的日志大小,并不是上面说的totalSizeCap
存在必要性:如果之后对日志的处理程序对日志的大小由要求,那么就可以使用该policy。
TimeBasedRollingPolicy中的totalSizeCap是用来限制所有的存档日志的累计大小的。而当前介绍的policy可以限制单个日志文件的大小
二:源代码
从上面的类定义可以看到SizeAndTimeBasedRollingPolicy是继承于TimeBasedRollingPolicy,所以可以理解为是TimeBasedRollingPolicy的一个扩展
三:配置项
- fileNamePattern:该配置项除了支持https://blog.csdn.net/javadocdoc/article/details/123342235这里介绍的,还增加了一个标识符%I。且在SizeAndTimeBasedRollingPolicy中,%i和%d都是必选的。%I指的是归档文件的序号,从0依次递增
- maxFileSize:指的是每一个文件日志的大小阀值,当超过该值的时候,当前的活跃日志文件mylog.txt就会归档,并且会生成一个新的日志文件mylog.txt以供接下来的日志写入
归档日志文件的文件名,mylog-%d{yyyy-MM-dd}.%i.txt,其中的%i就是在满足日期的当天生成的日志文件的自增索引,从0开始
四:使用的简单示例
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>
如上面的配置文件:
- 定义了一个名为ROLLING的RollingFileAppender。
- 指定了file配置项的值为mylog.txt,即日志的实时输出文件为mylog.txt
- 定义了rollingPolicy为SizeAndTimeBasedRollingPolicy,即基于时间和大小来触发日志流转的策略
- fileNamePattern为mylog-%d{yyyy-MM-dd}.%i.txt,等价于mylog-%d.%i.txt。即归档日志文件的文件名为logFile.加日期.log
- maxFileSize为100MB,即单个日志文件的大小最大为100MB
- maxHistory为60,即指保留近60天的日志
- totalSizeCap为20GB,即归档日志文件最多只能保留20GB(maxHistory比totalSizeCap优先级高)
- encoder,指定了日志输出的格式
- 设置root的日志级别为DEBUG,使用ROLLING的appender进行输出
在刚启动的时候,日志文件是输出到 mylog.txt中,当大小达到100MB的时候。mylog.txt重命名为mylog-2022-03-08.0.txt,且新创建一个mylog.txt文件,以供新的数据写入。待 mylog.txt的大小又达到100MB的时候。mylog.txt重命名为mylog-2022-03-08.1.txt,且新创建一个mylog.txt文件,以供新的数据写入。那么下一次就是重命名为mylog-2022-03-08.2.txt。fileNamePattern中的%I遵守从0开始依次递增的规则
TODO: 官方文档说:当应用程序暂停后重新启动,归档文件会从归档文件的最大索引开始继续归档,但是在测试的时候发现。重新启动的应用程序,是从0开始进行归档的
4.4.3 FixedWindowRollingPolicy
一:介绍
在由指定的trigger触发之后,日志文件的流转策略是:
- 保留的日志文件个数是在一个固定窗口中[minIndex,maxIndex] 中
二:源代码
可以看到FixedWindowRollingPolicy只继承了RollingPolicyBase,所以在配置使用的时候。需要在配置文件制定一个trigger结合使用
即FixedWindowRollingPolicy只实现了日志流转的功能,和上面介绍的几种的差别在于没有实现触发的功能
三:支持的配置项
- minIndex: int类型,标识创建的下界
- maxIndex:int类型,标识创建的上界
- fileNamePattern: 日志文件归档是的文件名命名格式,必须要包含%i。%i代表的是当前归档文件在窗口范围内的索引。例如,minIndex=1,maxIndex=3的时候,那么MyLogFile%i.log 生成的归档文件就是MyLogFile1.log, MyLogFile2.log 和MyLogFile3.log。如果想要对归档文件进行压缩,那么就在pattern的后面增加.gz便可以触发
由于固定窗口policy会执行窗口大小即(maxIndex-minIndex+1)次文件重命名操作,所以不建议设置的太大。所以如果设置超过了20,logback就会自动按20计算
滚动次数 | 活跃日志文件 | 归档日志文件 | 描述 |
---|---|---|---|
0 | foo.log | 尚未有日志滚动,所有的日志都输出到初始日志文件 | |
1 | foo.log | foo1.log | 发生了第一次的滚动,foo.log被重命名为foo1.log。并且创建一个新的foo.log以供接收新的日志输出 |
2 | foo.log | foo1.log fool2.log | 发生了第二次的滚动,foo1.log被重命名为foo2.log,foo.log被重命名为foo1.log。并且创建一个新的foo.log以供接收新的日志输出。接下来如果再发生日志滚动,那么就会删除foo2.log,然后按照上一步进行日志的重命名 |
四:简单示例
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>test.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>tests.%i.log.zip</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>
如上配置文件,
- 定义了名为FILE的RollingFileAppender
- 日志流转的触发策略是SizeBasedTriggeringPolicy,即基于日志文件的大小进行触发
- maxFileSize设置为5MB,即单个文件的大小为5MB
- 日志流转策略为FixedWindowRollingPolicy
- fileNamePattern为tests.%i.log.zip。%I为[minIndex,maxIndex]。且采用了zip压缩
4.5 trigger
可以看到在trigger接口中有1个方法,isTriggeringEvent()有两个参数,分别是生效文件和当前要处理的event。这个方法主要是根据这些参数来决定是否要滚动日志文件
4.5.1 TimeBasedRollingPolicy
TimeBasedRollingPolicy 作为最常用的policy在上面已经介绍过了,这里不做详述
4.5.2 SizeBasedTriggeringPolicy
一:介绍
基于日志文件的大小进行滚动的触发条件,当当前活动文件的大小超过设置值的时候,就会通知RollingFileAppender可以进行日志滚动了
二:配置项
- maxFileSize,默认值为10MB。maxFileSize可以通过多种单位来指定,bytes,kb,mb,gb。都可以支持。例如5000000, 5000KB, 5MB and 2GB 这些都是有效值,其中5000000==5MB
三:源代码
四:使用示例
<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} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>