logback原理与配置

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

  1. Appender控制日志输出方式,可以输出到控制台、文件、远程socket服务器、mysql、PostgreSQL、oracle以及其他数据库、JMS、远程Unix系统日志

  2. 多个appender可以附加到一个logger上,一个给定的logger接收到日志请求后,它会将请求转发到它下面的appender以及继承层次更高的appender之上。Appenders也具有继承性。举个例子,一个控制台输出的appender加入到root logger下面,那么所有有效日志请求都会输出到控制台。如果文件appender加入到logger A,那么A以及A的孩子,在收到日志请求后,会将日志输出到文件以及控制台上

  3. 通过设置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配置寻找

  1. classpath下的logback.xml
  2. classpath下的logbac.groovy
  3. classpath下的logback.xml
  4. 通过service-provider loading facility来解析com.qos.logback.classic.spi.Configuration接口实现,这个实现的全名称配置在META-INF\services\ch.qos.logback.classic.spi.Configurator里面
  5. 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。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

微信扫一扫

微信扫一扫

微信扫一扫,分享到朋友圈

logback原理与配置