Logback体系结构
Logback体系结构
Logback包括三个模块,logback-core、logback-classic、logback-access
Logback-core是另外两个模块的基础,classic模块继承自core模块,classic模块对应于log4j的改进版本,由于Logback-classic模块实现了SLF4jAPI,可以轻松的切换日志框架,比如log4j或者Java.util.logging,
Log-access模块与Servlet容器集成可以提供HTTP访问日志,
Logger, Appenders and Layouts
Logback建立于三个主要的类之上:Logger、Appender以及Layout.这三个类齐心协力可以使开发者根据日志类型、日志级别输出日志信息,控制输出格式以及控制输出位置
Logger context
Logback会管理它下面的各个Loggers,Logger具有继承关系,通过点号,来分割名称,如果一个Logger a名称是另外一个Logger b名称的前缀,那么a是b的祖先
、
日志继承级别
给定一个Logger,它的日志级别是沿着继承层次向上找一个非空的日志级别,继承层次最高的是root logger
下面给定几个例子来说明这个机制。
Example 1
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | none | DEBUG |
X.Y | none | DEBUG |
X.Y.Z | none | DEBUG |
Example 2
Logger name | Assigned level | Effective level |
---|---|---|
root | ERROR | ERROR |
X | INFO | INFO |
X.Y | DEBUG | DEBUG |
X.Y.Z | WARN | WARN |
Example 3
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | none | INFO |
X.Y.Z | ERROR | ERROR |
Example 4
Logger name | Assigned level | Effective level |
---|---|---|
root | DEBUG | DEBUG |
X | INFO | INFO |
X.Y | none | INFO |
X.Y.Z | none | INFO |
日志输出选择规则
日志级别有5个,分别为TRACE,DEBUG,INFO,WARN,ERROR,以级别大小排序是TRACE<DEBUG<INFO<WARN<ERROR,如果日志级别设置为q,代码中输出级别是p,只有p>=q,日志才会真正输出出来
例如:设置日志输出级别为INFO级别,代码输出为DEBUG,那么日志不会输出
将代码输出的日志级别调整为ERROR后可以正常输出:
Appenders and Layouts
-
Appender控制日志输出方式,可以输出到控制台、文件、远程socket服务器、mysql、PostgreSQL、oracle以及其他数据库、JMS、远程Unix系统日志
-
多个appender可以附加到一个logger上,一个给定的logger接收到日志请求后,它会将请求转发到它下面的appender以及继承层次更高的appender之上。Appenders也具有继承性。举个例子,一个控制台输出的appender加入到root logger下面,那么所有有效日志请求都会输出到控制台。如果文件appender加入到logger A,那么A以及A的孩子,在收到日志请求后,会将日志输出到文件以及控制台上
-
通过设置appender additivity为false,可以取消appender的继承性
下面的表格说明了,通过设置这个flag改变了appender继承性
Logger Name | Attached Appenders | Additivity Flag | Output Targets | Comment |
---|---|---|---|---|
root | A1 | not applicable | A1 | Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it. |
x | A-x1, A-x2 | true | A1, A-x1, A-x2 | Appenders of “x” and of root. |
x.y | none | true | A1, A-x1, A-x2 | Appenders of “x” and of root. |
x.y.z | A-xyz1 | true | A1, A-x1, A-x2, A-xyz1 | Appenders of “x.y.z”, “x” and of root. |
security | A-sec | false | A-sec | No appender accumulation since the additivity flag is set to false. Only appender A-sec will be used. |
security.access | none | true | A-sec | Only appenders of “security” because the additivity flag in “security” is set to false. |
修改日志输出格式
通常默认的日志输出样式可能不是我们想要的,Logback提供了很多日志样式参数,可以按照我们的需求来输出日志。控制日志输出样式的是PatternLayout
例如,转化模式是”%-4relative [%thread] %5-level %logger{32} - %msg%n”将会输出如下内容
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
- 第一个字段是从程序启动到打印这条日志所花时间
- 第二个字段是提出日志请求的线程名
- 第三个字段是日志请求级别,
- 第四个字段是logger名称
- 第五个字段是日志输出内容
Logback推荐在日志输出之前做一次日志能否输出的判断,
if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
如果少了判断,直接输出,那么无论日志是否输出,都会有构造信息参数的花费,将i转换为字符串,array数组转换为字符串
加上判断,即使日志真的会输出,这个判断的代价是很小的,可以忽略不计
推荐使用占位符的方式发起日志请求
例如
Object entry=new SomeObject();
Logger.debug(“the entry is {}”,entry);
比 logger.debug(“the entry is “+entry+””)的方式更好
前者比后者快百分之30左右
Logback配置
Logback配置寻找
- classpath下的logback.xml
- classpath下的logbac.groovy
- classpath下的logback.xml
- 通过service-provider loading facility来解析com.qos.logback.classic.spi.Configuration接口实现,这个实现的全名称配置在META-INF\services\ch.qos.logback.classic.spi.Configurator里面
- Logback使用默认的BasicConfiguration,默认直接输出到控制台上
快启动:
解析用户自定义的xml文件需要100ms的时间,如果这个时间占用了系统启动时间,可以通过service-provider loader facility的方式在一个合适的启动点加载配置文件
如果没有指定任何配置文件,那么logback会调用BasicConfiguration来最小化配置,它会将ConsoleAppender附加到root logger,输出格式使用PatternLayoutEncoder来设置输出格式为%d{HH:mm:ss.SSS} [%thread]
%-5level %logger{36} - %msg%n. 16:06:09.031 [main] INFO
chapters.configuration.MyApp1 - Entering application. 16:06:09.046
[main] DEBUG chapters.configuration.Foo - Did it again! 16:06:09.046
[main] INFO chapters.configuration.MyApp1 - Exiting application.
使用logback-test.xml或者logback.xml自动配置
默认会使用logback.xml文件或者logback-test.xml文件,如果二者都不存在时,使用BasicConfiguration。
这个类等同于如下配置
<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>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
出现warning和error时会自动打印状态信息,也可以使用logback使用的api来打印状态信息,具体如下:
public static void main(String[] args) {
// assume SLF4J is bound to logback in the current environment
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
// print logback's internal status
StatusPrinter.print(lc);
...
}
运行上述程序,可以看到idea输出如下信息:
09:43:28,939 |-INFO in ch.qos.logback.classic.LoggerContext[default] -
Could NOT find resource [logback.groovy] 09:43:28,939 |-INFO in
ch.qos.logback.classic.LoggerContext[default] - Could NOT find
resource [logback-test.xml] 09:43:28,940 |-INFO in
ch.qos.logback.classic.LoggerContext[default] - Found resource
[logback.xml] at
[file:/E:/ideaWorkspace/testlog/target/classes/logback.xml]
09:43:29,061 |-INFO in
ch.qos.logback.classic.joran.action.ConfigurationAction - debug
attribute not set 09:43:29,112 |-INFO in
ch.qos.logback.core.joran.action.AppenderAction - About to instantiate
appender of type [ch.qos.logback.core.ConsoleAppender] 09:43:29,122
|-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming
appender as [STDOUT] 09:43:29,156 |-INFO in
ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming
default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for
[encoder] property 09:43:29,262 |-INFO in
ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level
of ROOT logger to DEBUG 09:43:29,262 |-INFO in
ch.qos.logback.core.joran.action.AppenderRefAction - Attaching
appender named [STDOUT] to Logger[ROOT] 09:43:29,263 |-INFO in
ch.qos.logback.classic.joran.action.ConfigurationAction - End of
configuration. 09:43:29,266 |-INFO in
ch.qos.logback.classic.joran.JoranConfigurator@1ccd51b - Registering
current configuration as safe fallback point
命令行指定配置文件:
Java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
不过命令行方式,只能识别.xml文件或者.groovy文件,其他格式文件将被忽略
通过代码的方式指定配置文件
public class ServerMain {
public static void main(String args[]) throws IOException, InterruptedException {
// must be set before the first call to LoggerFactory.getLogger();
// ContextInitializer.CONFIG_FILE_PROPERTY is set to "logback.configurationFile"
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, /path/to/config.xml);
...
}
}
需要注意的是,指定系统属性之后,再创建日志实例,才会使设置系统属性生效
自动重载配置文件
Logback默认不会自动重载配置文件,不过通过在配置文件中加入scan属性,可以使logback周期性的加载配置文件
如下
<configuration scan="true">
...
</configuration>
默认情况下,logback会每分钟加载一次配置文件,如果需要更改时间频率,可以使用scanPeriod属性配置
<configuration scan="true" scanPeriod="30 seconds" >
...
</configuration>
配置文件默认的时间单位是毫秒
在堆栈跟踪信息中输出包信息
在1.1.4版本中,包信息不会被输出
如果设置为输出,输出信息如下:
14:28:48.835 [btpool0-7] INFO c.q.l.demo.prime.PrimeAction - 99 is
not a valid value java.lang.Exception: 99 is invalid at
ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28)
[classes/:na] at
org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
[struts-1.2.9.jar:1.2.9] at
org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)
[struts-1.2.9.jar:1.2.9] at
org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
[struts-1.2.9.jar:1.2.9] at
javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
[servlet-api-2.5-6.1.12.jar:6.1.12] at
org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
[jetty-6.1.12.jar:6.1.12] at
ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44)
[classes/:na] at
org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115)
[jetty-6.1.12.jar:6.1.12] at
org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361)
[jetty-6.1.12.jar:6.1.12] at
org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
[jetty-6.1.12.jar:6.1.12] at
org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
[jetty-6.1.12.jar:6.1.12
可以在configuration标签加入packagingData属性的方式输出包信息。
<configuration packagingData="true">
...
</configuration>
通过代码的方式:
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);
配置文件语法规则
Logbac配置文件语法是很灵活的,可以通过xml文件的方式进行定义,最基础的定义方式是元素下有0个或者多个元素,紧接着0个或者多个元素,紧接着最多一个元素
root是特殊的logger,它是顶级logger,它上面所有的appender会被它下面的logger所继承,它下面的logger如果没有声明日志输出级别,那么也会继承这个日志级别,如果设置了日志输出级别,使用子logger使用自己的日志输出级别
举例说明这个规则:
Idea项目目录结构如下:
Logback.xml里面内容如下:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.xyz" level="INFO">
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Testhierarchy类中内容如下:
package com.xyz.log.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Testhierarchy {
public static void main(String[] args) {
Logger logger=LoggerFactory.getLogger(Testhierarchy.class);
logger.info("test info");
logger.debug("test debug");
logger.error("test error");
logger.warn("test warn");
}
}
对应输出如下:
TestHierarchy类中LoggerFactory创建出来的Logger对象在配置文件中寻找Logger层级,找到了包定义为com.xyz的logger A,A的日志输出级别为INFO,Appender从root继承,是STDOUT,输出到控制台。结果验证了这一规则
修改logback.xml文件,加入一个新的logger
<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>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.xyz" level="INFO">
</logger>
<logger name="com.xyz.log" level="DEBUG">
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
再次运行,得到如下结果
DEBUG级别以上日志都输出到了控制台,说明Testhierarchy中的logger根据配置文件定义的logger,找到了包定义为com.xyz.log的logger作为父logger,继承了日志输出级别为DEBUG,appender为STDOUT,并输出到控制台。
配置Appenders
Appender通过元素进行定义,通常包括两个属性name和class,name属性标志这个appender,class属性指定这个appender实现的全限定名。元素必须包括0个或者1个元素,0个或者多个元素,0个或者多个元素。
下面这个图说明了整体的架构
元素也可以指定多种实现方式,只需要设置实现类的全限定名,在不设置的情况下,默认是PatternLayout。
元素同样如此,默认实现是PatternLayoutEncoder
在logback配置文件中可以设置多个appender,同时挂到一个logger上。
举例如下:
Logback.xml文件中内容如下:
test.log
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE"/>
</root>
定义两个Appender,一个输出到控制台,一个输出到文件,两个Appender都加到root上
定义一个测试类TestMultiOutput类内容如下,
package com.xyz.log.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestMultiOutput {
public static void main(String[] args) {
Logger logger= LoggerFactory.getLogger(TestMultiOutput.class);
logger.debug("test Multi-output");
}
}
观察输出结果:
控制台输出为:
文件test.log输出如下:
Appenders叠加性
子logger具有一个appender A,父logger比如root也具有这个Appender A。这样子logger会有两个Appender A。
修改logback.xml文件,
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.xyz" level="debug">
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
新增一个TsetCumulative类,内容如下:
package com.xyz.log.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestCumulative {
public static void main(String[] args) {
Logger logger= LoggerFactory.getLogger(TestCumulative.class);
logger.debug("test cumulative");
}
}
最终运行结果如下:
取消appenders叠加性的方式是通过设置logger的additivity属性为false
修改locback.xml文件
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="com.xyz" level="debug" additivity="false">
<appender-ref ref="STDOUT" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
运行输出,可以得到如下结果:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190926150304927.png)
变量定义
通过元素定义
设置appender日志文件输出路径时,将文件的目录设置为一个变量
在Appender中只要使用这个变量就好了
<configuration>
<property name="OUTPUT_DIR" value="/home/test " />
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${OUTPUT_DIR}/test.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
</root>
</configuration>
这样输出日志时,会将日志输出到/home/test/test.lgo中进行保存
也可以通过命令行的方式传入变量
Java -DOUTPUT_DIR=”/home/test” Test
如果参数有很多个,可以通过配置文件的方式引入变量。配置文件中可以这样定义变量
OUTPUT_DIR =/home/test
变量的作用域
变量作用域分为三个级别,local、context、system
Local:变量只在配置文件内部有效
Context: 这个变量在应用上下文中有效,例如在日志事件中
System:JVM系统属性级别
配置文件支持条件判断
日志输出配置通常在测试环境、开发环境、生产环境有不同的输出,如果每个环境都配置一个logback.xml文件,那么会显得比较臃肿,logback配置文件支持条件判断的方式定义xml文件
<!-- if-then form -->
<if condition="some conditional expression">
<then>
...
</then>
</if>
<!-- if-then-else form -->
<if condition="some conditional expression">
<then>
...
</then>
<else>
...
</else>
</if>
条件判断的参数只能是context级别或者system级别,通过property()方法获取这个参数值,
isDefined()方法可以用来检查一个属性是否被定义,isNull()方法可以来检测参数是否为空
<configuration debug="true">
<if condition='property("HOSTNAME").contains("torino")'>
<then>
<appender name="CON" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root>
<appender-ref ref="CON" />
</root>
</then>
</if>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${randomOutputDir}/conditional.log</file>
<encoder>
<pattern>%d %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="ERROR">
<appender-ref ref="FILE" />
</root>
</configuration>
Appenders
Appender定义
:负责将写日志事件的任务下发到各个component、
实现方式:实现ch.qos.logback.core.Appender接口
package ch.qos.logback.core;
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.FilterAttachable;
import ch.qos.logback.core.spi.LifeCycle;
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {
public String getName();
public void setName(String name);
void doAppend(E event);
}
其中最为重要的方法是doAppend()方法,这个方法负责将日志事件以一种合适的格式输出到一个输出设备中
Appenders负责最终输出日志事件。不过它会将这个日志事件下发到event或者layout中,让它们来格式化输出。每一个layout/encoder只能关联一个appender。一些appenders有固定内置的时间格式,这样他们就不需要layout/encoder。举例说明,SocketAppender会在传输数据之前将日志事件序列化
Logback-core
Logback-core是其他模块的基石,logback-core中的模块支持扩展,通过暴露一些参数,用户可以设置自已喜好的风格
OutputStreamAppender
OutpuStreamAppender可以将时间添加到java.io.OutputStream中,这个类为其他appender提供了基本的功能,下面是基本的配置参数
属性名 | 类型 | 描述 |
---|---|---|
encoder | Encoder | 决定日志输出到OutputStreamAppender的方式 |
immediateFlush | Boolean | 默认值为true,保证日志事件可以马上写出,而且不会因为应用的退出而导致数据丢失,这个参数设置为false,可以提高接近4倍的日志吞吐量,但是可以在应用意外退出时,没有输出到硬盘的数据会丢失 |
下面有一个关于Appender的继承层次类图
ConsoleAppender
ConsoleAppender可以将日志输出到控制台,通过System.out或者System.err的方式,System.out是默认方式。ConsoleAppender通过用户设置的encoder格式化日志输出样式
下面是它的一些配置参数
参数名 | 参数类型 | 描述 |
---|---|---|
encoder | Encoder | 决定输出的样式 |
target | String | 选择使用System.out还是System.err输出,默认是System.out |
withJansi | boolean | 默认为false,如果设置true,可以支持ANSI颜色的代码,不过windows下需要导入org.fusesource.jansi:jansi:1.8jar包 |
ConsoleAppender配置实例:
<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>
FileAppender
FileAppender是OutputStreamAppender的子类,可以将日志写到文件中去,文件位置可以通过File选项指定,支持追加或者覆盖的方式。
下面是它的一些常用属性
属性名 | 类型 | 描述 |
---|---|---|
append | boolean | 默认为true,采用追加方式写入文件,false的话会覆盖写入 |
encoder | Encoder | 决定日志输出样式 |
file | String | 定义日志输出到文件的名称,如果文件不存在,会进行创建,需要注意的是,在Windows机器上,文件名要注意转义符,比如c:\temp\test.log,中两个\t会翻译为tab键,如果要想要的文件,可以将\替换为\或者/. |
prudent | boolean | 在prudent模式下,FileAppender会很安全的输出到特定文件,即使有运行在其他JVM上的FileAppender实例也输出到同一个文件中,默认prudent模式是false.Prudent模式下文件以追加方式进行日志写入Prudent模式依赖文件排他锁,这样会导致比简单的写一个日志输出事件多三倍的性能消耗,当prudent模式关闭,每秒钟可以有100000个事件处理吞吐量,打开的话就只剩下33000。每秒产生100个或者更多的IO操作的应用应该避免使用prudent模式 |
immediateFlush | boolean | 默认为true,可以保证日志输出不会因为应用的意外退出而丢失数据,如果丢失数据是可以容忍的,那么将这个参数设置为false,可以提高日志吞吐量 |
FileAppender配置实例
<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>
设置文件名(时间戳)
在应用开发阶段或者短声明周期的应用,需要每次应用启动时产生新的日志文件,使用元素可以轻松的解决这个问题
<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>
timestamp元素有两个必须要写的属性key和datePattern,以及一个可选属性timeReference。Key属性可以作为timestamp的名称,下面的配置可以使用这个名称进行引用这个timestamp,dataPattern属性将当前时间转换为特定样式的字符串。date定义方式遵从SimpleDateFormat。
timeReference决定使用哪个时间,比如想使用应用启动时间,那么可以将timeReference属性设置为contextBirth
<configuration>
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"
timeReference="contextBirth"/>
...
</configuration>
RollingFileAppender
RollingFileAppender继承于FileAppender,主要作用是为了回滚日志文件,比如,RollingFileAppender可以在某些条件满足的情况下将日志写入到一个log.txt文件当中,改变输出目标到另外一个文件
RollingFileAppender有两个重要的子模块,RollingPolicy和TriggeringPolicy,RollingPolicy负责回滚时需要做的事情,TriggeringPolicy则负责什么时候触发一次回滚
RollingFileAppender需要同时设置RollingPolicy和TriggeringPolicy,如果RollingPolicy实现了TriggeringPolicy接口,那么只需要显示的设置RollingPolicy就好了
下面是RollingFileAppender的属性:
属性名 | 类型 | 描述 |
---|---|---|
file | String | 和FileAppender的file一样 |
append | boolean | 和FileAppender的append一样 |
encoder | Encoder | 和OutputStreamAppender一样 |
rollingPolicy | RollingPolicy | 指定RollingFileAppender的行为当发生了回滚时 |
triggeringPolicy | TriggeringPolicy | 什么时候触发回滚 |
prudent | boolean | FixedWindowRollingPolicy不支持 RollingFileAppender使用TimeBasedRollingPolicy时,使用prudent模式会有两个限制:1. 在prudent模式中,文件压缩不支持2. FileAppender的file属性不能指定,因为多数操作系统不支持在另外一个线程打开它的时候更改文件名 |
TimeBasedRollingPolicy
TimeBasedRollingPolicy是最为常用的回滚策略,它基于时间定义回滚策略,比如可以按照天或者月。TimeBasedRollingPolicy同时负责触发回滚以及回滚操作,它同时实现了RollingPolicy和TriggeringPolicy接口
下面是它的一些常用的属性配置
属性名 | 属性类型 | 描述 |
---|---|---|
fileNamePattern | String | fileNamePattern指定了回滚日志文件名称,它应该由文件名称加上一个日期转换符%d.%d转换符可能包含一个date-and-time的模式定义,如果没有定义会使用默认的yyyy-MM-dd,也就是年月日的方式,回滚的周期是从fileNamePattern值推断而来 RollingFileAppender的file属性可以设置或者不设置,如果设置了file属性,那么可以分离日志文件以及压缩日志文件的位置,当前日志总是被输出到file属性定义的位置,当前日志文件不会更改自己的文件名。如果你不设置file属性名称,那么,日志文件名称会根据fileNamePattern计算出来date-and-time模式,是定义在%d{}花括号里面的,它根据SimpleDateFormat的方式进行定义,在fileNamePattern中/和\会被翻译为文件分隔符多个%d符号当有多个%d符号时,一个%d为主,其他为辅,主%d决定回滚周期,其他的%d被标记为备用的,以aux标记多个%d可以以目录结构的方式组织压缩文件。比如下面的模式定义组织日志目录以年月为基础,每天回滚一次日志/var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.logTimeZone指定回滚的时区,只需要传递时区参数到模式定义中,比如:aFolder/test.%d{yyyy-MM-dd-HH, UTC}.log |
maxHistory | int | maxHistory属性控制需要保存多久的日志文件 |
totalSizeCap | int | totalSizeCap属性控制所有日志文件的最大大小总和,当最大大小被超越时,最老的日志将会被删除。totalSizeCap属性需要maxHistory属性同时被设置,maxHistory属性会先起作用 |
cleanHistoryOnStart | boolean | 启动应用会清除所有旧的日志 |
fileNamePattern参数值
filenamePattern | 回滚调度 | 示例 |
---|---|---|
/wombat/foo.%d | 使用默认模式yyyy-MM-dd每天晚上做一次回滚 | file属性没有设置:2019年8月25号,日志会输出到/wombat/foo.2019-08-25,到了8月26号,日志输出到/wombat/foo.2019-08-26file属性设置为/wombat/foo.txt:2019年8号25号,日志会输出到/wombat/foo.txt,到了半夜,foo.txt会改名为/wombat/foo.2019-08-25。到了2019年8月26日,一个新的/wombat/foo.txt会再次创建出来,日志会输出到foo.txt,到了半夜会将日志改名为/wombat/foo.2019-08-26/wombat/%d{yyyy/MM}/fo.txt 每个月月初回滚 file属性没有设置:2019年8月期间,日志会输出到/wombat/2019/08/foo.txt,过了8月,到了9月份,日志会输出到/wombat/2019/09/foo.txtfile属性设置为/wombat/foo.txt.在8月份,日志会输出到/wombat/foo.txt到了31日半夜,日志会移动到/wombat/2019/08/foo.txt |
/wombat/foo.%d{yyyy-ww}.log | 每个星期开始回滚一次日志 | 同上 |
/wombat/foo%d{yyyy-MM-dd_HH}.log | 每个小时开始回滚一次 | 同上 |
/wombat/foo%d{yyyy-MM-dd_HH-mm}.log | 每分钟开始回滚一次 | 同上 |
/foo/%d{yyyy-MM,aux}/%d.log | 每天回滚一次,压缩文件位于年月下面 | 第一个%d被标注为辅助%d,第二个%d省略了具体模式,这样默认会每天回滚一次日志,并且日志文件会在年月组成的二级目录下面 |
TimeBasedRollingPolicy文件压缩
TimeBasedRollingPolicy支持自动文件压缩,如果fileNamePattern选项以.gz或者.zipj结尾,那么将会启用压缩
文件名模式 | 回滚调度 | 实例 |
---|---|---|
/wombat/foo.%d.gz | 每天回滚一次,并且使用GZIP来压缩文件 | file属性没有设置:2019年8月25日,日志输出到/wombat/foo.2019-08-25,到了半夜,日志文件压缩为/wombat/foo.2019-08-25.gz。到了26日,日志输出到/wombat/foo.2019-08-26file属性设置为/wombat/foo.txt:在2019年08月25日,日志输出到/wombat/foo.txt,到了半夜,日志会压缩并更名为/wombat/foo.2019-08-25.gz,到了26日,新的/wombat/foo.txt文件会被创建出来,并且日志会输出到这个新的文件上面,到了26日半夜,/wombat/foo.txt会被压缩且更名为/wombat/foo.2019-08-26.gz以此类推 |
fileNamePattern属性有两方面的作用:第一,计算回滚周期,其二,计算压缩文件名。
两种不同的fileNamePattern属性可能有相同的回滚周期,但是文件名不一样,比如: yyyy-MM和yyyy@MM
通过同时设置file属性,可以拥有两个日志存放位置,一个是有效日志文件的位置,另外一个是日志压缩文件的位置。
日志压缩的时机不是真正基于设置的时间,而是日志事件到达的时机,比如晚上23点30分到24点0分这段时间没有任何日志事件到来,那么触发日志压缩的时间点会是23点30分。
TimeBasedRollingPolicy配置实例:
logFile.log
logFile.%d{yyyy-MM-dd}.log
<!-- 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>
SizeAndTimeBasedRollingPolicy
通过日期压缩日志文件的同时限制每个日志文件的大小。
<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>
FixedWIndowRollingPolicy
定长窗口回滚策略会根据每轮回滚,修改日志文件名,保持回滚日志总数量,新文件按照fileNamePattern定义的名字新建,旧的日志名进行滑动修改。
下面是FixedWindowRollingPolicy的一些属性:
属性名 | 属性类型 | 描述 |
---|---|---|
minIndex | int | 窗口索引的最小值 |
maxIndex | Int | 窗口索引的最大值 |
fileNamePattern | String | 代表FixedWindowRollingPolicy在更名日志文件时使用的模式,必须包括%d,代表插入到当前窗口的索引位置。 |
例如,使用MyLogFile%i.log作为模式,窗口最小值1,最大值3,经过三轮的回滚,那么将会产生MyLogFile1.log,MyLogFile2.log以及MyLogFile3.log
压缩选项也可以通过模式指定,比如将fileNamePattern设置为MyLogFile%i.log.zip意味着压缩文件将会被压缩为zip格式,gz格式也是支持的
大的窗口是不建议使用的,当窗口大于20时,现有的实现会将窗口缩小到20.
这里有具体的例子,将窗口的minIndex设置为1,maxIndex设置为3,fileNamePattern属性设置为foo%i.log,file属性设置为foo.log
日志回滚次数 | 日志输出目标 | 压缩日志文件 | 描述 |
---|---|---|---|
0 | foo.log | - | 没有发生日志回滚,直接输出到foo.log |
1 | foo.log | foo1.log | 第一次回滚,foo.log更名为foo1.log,一个新的foo.log文件被创建,并成为输出目标 |
2 | foo.log | foo1.log, foo2.log | 第二轮回滚,foo1.log更名为foo2.log,foo.log更名为foo1.log。新的foo.log创建,并成为新的输出目标 |
3 | foo.log | foo1.log foo2.log, foo3.log | 第三轮回滚,foo2.log更名为foo3.log,foo1.log更名为foo2.log,foo.log更名为foo1.log。新的foo.log创建,并成为日志输出目标 |
4 | foo.log | foo1.log,foo2.log,foo3.log | 第四轮日志回滚,删除foo3.log,其他日志文件将会依次递增自己的文件名,新的日志文件foo.log创建,并成为输出目标 |
FixedWindowRollingPolicy配置示例
<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>
Triggering policy
TriggeringPolicy实现负责在合适的时候让RollingFileAppender进行日志回滚
TriggeringPolicy接口只包括一个方法
package ch.qos.logback.core.rolling;
import java.io.File;
import ch.qos.logback.core.spi.LifeCycle;
public interface TriggeringPolicy<E> extends LifeCycle {
public boolean isTriggeringEvent(final File activeFile, final <E> event);
}
isTriggeringEvent()方法接受日志文件,以及日志事件作为参数,具体的实现决定了日志回滚是否会发生。
最常用的日志触发策略是TimeBaseRollingPolicy,同时它也是一个回滚策略
SizeBasedTriggeringPolicy
SizeBasedTriggeringPolicy观察当前日志文件的大小,如果它的大小超过了特定大小,那么它会释放一个信号提示RollingFileAppender应该对现有的日志文件进行一次回滚
SizeBasedTriggeringPolicy只接受一个参数,maxFileSize,默认大小为10MB
可以自定义文件大小,比如5000000,5000KB,5MB和2GB等等
下面是RollingFileAppender配合使用SizebasedTriggeringPolicy的配置,它会让日志文件达到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>
此外还有ServerSocketAppender、SSLServerSocketAppender、SMTPAppender、DBAppender、SyslogAppender、SiftingAppender、AsyncAppender。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。