Slf4j与Logback的使用教程以及在Spring Boot下的应用

  • Post author:
  • Post category:其他




第一章 slf4j & log4j & logback的区别



1.1 slf4j



1.1.1 slf4j概念

SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志System.

实际上,SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。从某种程度上,SLF4J有点类似JDBC,不过比JDBC更简单,在JDBC中,你需要指定驱动程序,而在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用那个具体的日志系统。如同使用JDBC基本不用考虑具体数据库一样,SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。



1.1.2 使用场景


如果你开发的是公共使用的JAR包、类库、公共组件,那么就应该考虑采用SLF4J,因为不可能影响最终用户选择哪种日志系统。

在另一方面,如果是一个简单或者独立的应用,确定只有一种日志系统,那么就没有使用SLF4J的必要。假设你打算将你使用log4j的产品卖给要求使用logback的用户时,面对成千上万的log4j调用的修改,相信这绝对不是一件轻松的事情。但是如果开始便使用SLF4J,那么这种转换将是非常轻松的事情。



1.2 log4j & logback

log4j和logback就是两个受欢迎的日志框架。但两者又有不同。

log4j是apache实现的一个开源日志组件。

logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架。是slf4j的原生实现。

无论从设计上还是实现上,Logback相对log4j而言有了相对多的改进。但是两者的用法几乎差别不大。下面是logback的优势:

1.更快的执行速度

2.充分的测试

3.logback-classic 非常自然的实现了SLF4J

4.丰富的扩展文档

5.可以使用使用XML配置文件或者Groovy

6.自动重新载入配置文件

7.优雅地从I/O错误中恢复

8.自动清除旧的日志归档文件

9.自动压缩归档日志文件



第二章 slf4j使用



演示代码工程:

https://github.com/chutianmen/Slf4j-Logback-samples/slf4j-examples



2.1 Hello World

依赖:

<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.7.12</version>
</dependency>

延续编程领域的传统,这里用一个例子说明使用SLF4J最简单的方式输出”Hello world”。首先,获取一个HelloWorld类的logger日志对象。这个logger对象反过来用于记录”Hello World”消息。

代码:chapters\HelloWorld

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

​ 为了运行这个例子,你首先需要下载slf4j的发布包,然后解压它。解压完后,将slf4j-api-1.7.12.jar添加到你的类路径中。

​ 编译并运行HelloWorld,在控制台将会有输出如下的结果:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

​ 打印这个警告,是因为slf4j没有在类路径找到绑定(的日志框架)。

​ 一旦你在类路径中添加了一个绑定(的日志框架),警告就会消失。假设你添加了slf4j-simple的MAVEN依赖。

<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-simple</artifactId>
   <version>1.7.12</version>
</dependency>

​ 编译并运行HelloWorld ,现在在控制台将输出如下的结果:

0 [main] INFO HelloWorld - Hello World



2.2 在部署时绑定日志框架

正如之前提到的,SLF4J支持不同的日志框架。SLF4J发布包附带的几个jar包被称为“SLF4J绑定”,每一个绑定对应一种支持的框架:



slf4j-log4j12-1.7.12.jar

绑定log4j 1.2版本,一个广泛使用的日志框架。也需要把log4j.jar包放到你的类路径中。


slf4j-simple-1.7.12.jar

绑定简单实现(的日志框架),这个简单的日志框架将所有的事件输出到System.err。只有INFO级别以上的消息才被打印。这个绑定在小型应用程序可能会很有用。


logback-classic-1.0.13.jar (requires logback-core-1.0.13.jar)

**本地实现。**有一些在SLF4J工程之外的与SLF4J绑定,例如logback本身就实现了SLF4J。

Logback的ch.qos.logback.classic.Logger类是SLF4J的[org.slf4j.Logger]接口的直接实现。因此,结合logback使用SLF4J节省很多内存和计算的开销。



2.3 声明日志的工程依赖

考虑到Maven的传递依赖规则,对于普通工程(不是库或者框架)声明日志依赖可以通过单一的依赖声明来完成。

log4j`:如果你想要使用log4j作为日志框架,你需要在pom.xml文件中声明”org.slf4j:slf4j-log4j12″作为依赖。除了slf4j-log4j12-1.7.12.jar,这会将slf4j-api-1.7.12.jar包连同log4j-1.2.17.jar包加入工程中。记住明确地声明log4j-1.2.17.jar或者slf4j-api-1.7.12.jar依赖,有时可能是必要的。例如,根据Maven就近依赖原则,为了强加一个正确的版本给上述的artifact,就需要明确地声明。如下所示:

<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
	<version>1.2.3</version>
</dependency>

在resources下增加log4j.xml配置文件:

<?xml version="1.0"  encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
    <appender name="myConsole" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern"
                   value="%-d{yyyy-MM-dd HH:mm:ss,SSS} [%c]-[%p] %m%n"/>
        </layout>
    </appender>
    <root>
        <priority value="debug"/>
        <appender-ref ref="myConsole"/>
    </root>
</log4j:configuration>

在POM配置文件中注释掉上一节配置的依赖,改为上面slf4j-log4j12依赖:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEg9p4IU-1581304468717)(mdpic/1571813698840.png)]

运行:

代码:chapters.HelloWorld

结果如下:

2019-10-23 14:56:07,223 [chapters.HelloWorld]-[INFO] Hello World


logback-classic

:如果你想要使用logback-classic作为日志框架,你需要在pom.xml文件中声明”ch.qos.logback:logback-classic”依赖。除了logback-classic-1.0.13.jar包,这会将slf4j-api-1.7.12.jar包连同logback-core-1.0.13.jar包加入工程中。记住明确地声明依赖logback-core-1.0.13或者slf4j-api-1.7.12.jar是没有错的,有时可能是必要的。例如,根据Maven就近依赖原则,为了强加一个正确的版本给上述的artifact,就需要明确地声明。如下所示:

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.7.22</version>
</dependency>

在resources下增加logback-test.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="6000000" debug="false">

   <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} %-5p [%t:%c{1}:%L] - %msg%n"/>

   <!-- 系统级配置文件 开始 -->
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <layout class="ch.qos.logback.classic.PatternLayout">
         <Pattern>${LOG_PATTERN}</Pattern>
      </layout>
   </appender>

   <root level="debug">
      <!-- 本地测试时使用,将日志打印到控制台,实际部署时请注释掉 -->
      <appender-ref ref="STDOUT" />
   </root>
</configuration>

在POM配置文件中注释掉上一节配置的依赖,改为上面logback-classic依赖:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZWw8DHA-1581304468718)(mdpic/1571814073494.png)]

运行:

代码:chapters.HelloWorld

结果如下:

15:04:48.952 INFO  [main:c.HelloWorld:9] - Hello World



第三章 logback使用

Logback 继承自 log4j。

Logback 的架构非常的通用,适用不同的使用场景。Logback 被分成三个不同的模块:logback-core,logback-classic,logback-access。

logback-core 是其它两个模块的基础。logback-classic 模块可以看作是 log4j 的一个优化版本,它天然的支持 SLF4J,所以你可以随意的从其它日志框架(例如:log4j 或者 java.util.logging)切回到 logack。

logback-access 可以与 Servlet 容器进行整合,例如:Tomcat、Jetty。它提供了 http 访问日志的功能。

在应用程序当中使用日志语句需要耗费大量的精力。根据调查,大约有百分之四的代码用于打印日志。即使在一个中型应用的代码当中也有成千上万条日志的打印语句。考虑到这种情况,我们需要使用工具来管理这些日志语句。

以下是 logback 的初始化步骤:

  1. logback 会在类路径下寻找名为 logback-test.xml 的文件。
  2. 如果没有找到,logback 会继续寻找名为 logback.groovy 的文件。
  3. 如果没有找到,logback 会继续寻找名为 logback.xml 的文件。
  4. 如果没有找到,将会通过 JDK 提供的 ServiceLoader工具在类路径下寻找文件

    META-INFO/services/ch.qos.logback.classic.spi.Configurator

    ,该文件的内容为实现了 [

    Configurator

    ] 接口的实现类的全限定类名。
  5. 如果以上都没有成功,logback 会通过 [

    BasicConfigurator

    ] 为自己进行配置,并且日志将会全部在控制台打印出来。

最后一步的目的是为了保证在所有的配置文件都没有被找到的情况下,提供一个默认的(但是是非常基础的)配置。

如果你使用的是 maven,你可以在

src/test/resources

下新建 logback-test.xml。maven 会确保它不会被生成。所以你可以在测试环境中给配置文件命名为

logback-test.xml

,在生产环境中命名为

logback.xml



3.1 MAVEN配置



演示代码工程:

https://github.com/chutianmen/Slf4j-Logback-samples/logback-examples

在springmvc环境中,pom依赖如下:

<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.8.0-beta1</version>
</dependency>

<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-core</artifactId>
   <version>1.3.0-alpha2</version>
</dependency>

<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.3.0-alpha2</version>
</dependency>



3.2 参数化日志

考虑到 logback-classic 实现了 SLF4J 的 Logger 接口,一些打印方法可以接收多个传参。这些打印方法的变体主要是为了提高性能以及减少对代码可读性的影响。

对于一些 Logger 如下输出日志:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

会产生构建消息参数的成本,是因为需要将整数转为字符串,然后再将字符串拼接起来。但是我们是不需要关心 debug 信息是否被记录(强行曲解作者的意思)。

为了避免构建参数带来的损耗,可以在日志记录之前做一个判断,如下:

if(logger.isDebugEnabled()) { 
  logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

在这种情况下,如果

logger

没有开启 debug 模式,不会有构建参数带来的性能损耗。换句话说,如果 logger 在 debug 级别,将会有两次性能的损耗,一次是判断是否启用了 debug 模式,一次是打印 debug 日志。在实际应用当中,这种性能上的损耗是可以忽略不计的,因为它所花费的时间小于打印一条日志的时间的 1%。

有一种更好的方式去格式化日志信息。假设

entry

是一个 Object 对象:

SomeObject entryBean = new SomeObject();
logger.debug("The entry is {}", entryBean);

只有在需要打印 debug 信息的时候,才会去格式化日志信息,将 ‘{}’ 替换成 entry 的字符串形式。也就是说在这种情况下,如果禁止了日志的打印,也不会有构建参数上的性能消耗。

下面两行输出的结果是一样的,但是一旦禁止日志打印,第二个变量的性能至少比第一个变量好上 30 倍。

logger.debug("The new entry is " + entryBean + ".");
logger.debug("The new entry is {}", entryBean);

使用两个参数的例子如下:

logger.debug("The new entry is {}, It replaces {}.", entry, oldEntry);

如果需要使用三个或三个以上的参数,可以采用如下的形式:

Object[] paramArray = {newVal, below, above};
logger.debug("Value {} was inserted between {} and {}.", paramArray);

代码:logback-examples\chapters.configuration.ParasLog

package chapters.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ParasLog {
    private static final Logger logger = LoggerFactory.getLogger(ParasLog.class);

    public static void main(String[] args) {
        int i = 1;
        Object[] entry = {1,2,3};
        Object[] oldEntry = {4,5,6};
        if(logger.isDebugEnabled()) {
            logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
        }
        SomeObject entryBean = new SomeObject();
        logger.debug("The entry is {}", entryBean);

        logger.debug("The new entry is " + entryBean + ".");
        logger.debug("The new entry is {}", entryBean);

        logger.debug("The new entry is {}, It replaces {}.", entry, oldEntry);

        Object[] paramArray = {"newVal", "below", "above"};
        logger.debug("Value {} was inserted between {} and {}.", paramArray);
    }
}

日志输出:

Entry number: 1 is 2
Entry number: 1 is 2
The entry is {userName='jack', age=22}
The new entry is {userName='jack', age=22}.
The new entry is {userName='jack', age=22}
The new entry is [1, 2, 3], It replaces [4, 5, 6].
Value newVal was inserted between below and above.



3.3 配置文件详解

logback 允许你重新定义日志的行为而不需要重新编译代码,你可以轻易的禁用调应用中某些部分的日志,或者将日志输出到任何地方。

logback 的配置文件非常的灵活,不需要指定 DTD 或者 xml 文件需要的语法。但是,最基本的结构为

<configuration>

元素,包含 0 或多个

<appender>

元素,其后跟 0 或多个

<logger>

元素,其后再跟最多只能存在一个的

<root>

元素。基本结构图如下:

在这里插入图片描述



3.3.1 configuration:配置根节点

<configuration scan="true" scanPeriod="60" debug="false"></configuration> 


scan

:程序运行时配置文件被修改,是否重新加载。true,重新加载;false,不重新加载;默认为true;


scanPeriod

:监测配置文件被修改的时间间隔,scan属性必须设置为true才可生效;默认为1分钟,默认单位是毫秒;


debug

:是否打印logback程序运行的日志信息。true,打印;false,不打印;默认为false;



3.3.2 property:属性变量

logback 支持变量的定义以及替换。而且,变量可以在配置文件中,外部文件中,外部资源文件中,甚至动态定义。

<configuration scan="true" scanPeriod="60" debug="false">
    <property name="pattern" value="%d{HH:mm:ss.SSS} [%-5level] [%logger] %msg%n" >           </property>
</configuration>  


name

:变量的名称,可以随意起名,但建议名字要简明直译;


value

:变量的值;


Example

:variableSubstitution1.xml

<configuration>
	<property name="USER_NAME" value="/data/logs" />
    <property name="pattern" value="%d{HH:mm:ss.SSS} [%-5level] [%logger] %msg%n" />    

	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
		<file>${USER_NAME}/myApp.log</file>
		<encoder>
			<pattern>${pattern}</pattern>
		</encoder>
	</appender>
	
	<root level="debug">
		<appender-ref ref="FILE" />
	</root>	
</configuration>

修改”chapters.configuration.MyApp3“,把加载的配置文件改为”variableSubstitution1.xml“

String path = System.getProperty("user.dir") + "\\src\\main\\resources/variableSubstitution1.xml";

运行”chapters.configuration.MyApp3“,运行完成后,在当前硬盘目录下“data/logs”下生成文件”myApp.log“,如下所示:

在这里插入图片描述

当需要定义多个变量时,可以将这些变量放到一个单独的文件中。


Example

:variableSubstitution3.xml

<configuration>
	
	<property resource="resource1.properties" />
	
	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
		<file>${USER_HOME}/myApp.log</file>
		<encoder>
			<pattern>${PATTERN}</pattern>
		</encoder>
	</appender>
	
	<root level="debug">
		<appender-ref ref="FILE" />
	</root>
</configuration>

这个配置文件包含了一个对外部文件的引用:

variables1.properties

。这个外部文件包含一个变量:


Example

:resource1.properties

USER_HOME=/data/logs
PATTERN=%d{HH:mm:ss.SSS} [%-5level] [%logger] %msg%n

修改”chapters.configuration.MyApp3“,把加载的配置文件改为”variableSubstitution3.xml“

String path = System.getProperty("user.dir") + "\\src\\main\\resources/variableSubstitution3.xml";

运行”chapters.configuration.MyApp3“,运行完成后,运行结果同上面例子。



3.3.3 logger

<logger>

:日志对象

logger分为2种,一种是普通日志对象,另一种是根日志对象。对于大部分应用来说,只设置根日志对象即可。

在java日志系统中,无论是log4j还是logback,他们的日志对象体系都是呈现“树”的形式,根日志对象为最顶层节点,其余包或者类中的日志对象都继承于根日志节点;

对于普通日志对象来说,我们可以设置某一个包或者某一个类的日志级别,还可以单独设置日志的输出目的地;

<configuration scan="true" scanPeriod="60" debug="false">   
    <logger name="java.sql" level="debug" addtivity="true">
        <appender-ref ref="CONSOLE" />
    </logger>   
</configuration>  


name

:用来指定此logger属于哪个包或者哪个类;


level

:用来指定此logger的日志打印级别,level 属性的值可以为:TRACE、DEBUG、INFO、WARN、ERROR;


addtivity

:是否向上传递日志打印信息。之前说了,logger对象呈现一个树的结构,根logger是树的顶端,下面的子logger的addtivity属性如果设置为true则会向上传递打印信息,出现日志重复打印的现象;


appender-ref

:日志输出目的地,将此logger所打印的日志交给此appender;

值得注意的是,上面的例子中,如果此logger没有指定appender,而且addtivity也设置为true,那么此logger对应的日志信息只会打印一遍,是由root来完成的;但是如果addtivity设置成false,那么此logger将不会输出任何日志信息;



3.3.4 encoder

日志格式化节点,负责格式化日志信息。

<encoder>

只负责了两件事情,第一负责将日志信息转换成字节数组,第二将字节数组写到输出流当中去;



<encoder>

中使用

<pattern>

来设置对应的格式;

<encoder>   
    <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>   
</encoder> 


%logger

:表示输出logger名称,后面跟一个{0}表示:只输出logger最右边点符号之后的字符串;例如:com.jiaboyan.test —> test;


%d{HH:mm:ss.SSS}

:表示格式化日期输出,14:06:49.812;


%line

: 输出执行日志请求的行号。


%thread

:表示产生日志的线程名;


%level

:输出日志级别;


%method

:输出执行日志请求的方法名;


%class

:输出日志打印类的全限定名,后面跟{0}表示,含义为全限定类名最右边点符号之后的字符串。例如:com.jiaboyan.test —> test;


%-4relative

:符号减号“-”是左对齐 标志,接着是可选的最小宽度 修饰符,用十进制数表示。relative是输出从程序启动到创建日志记录的时间,单位是毫秒;


%msg

:表示应用程序提供需要打印的日志信息;


%n

:表示换行符;



3.1.5 logger

<root>

:根日志对象


<root>

也是日志对象中的一种,但它位于logger体系中的最顶层。当一个类中的logger对象进行打印请求时,如果配置文件中没有为该类单独指定日志对象,那么都会交给root根日志对象来完成;


<root>

节点中只有一个level属性,还可以单独指定日志输除目的地

<apender-ref>

;

<configuration scan="true" scanPeriod="60" debug="false">   
    <root level="DEBUG">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>


例子

如果我们不想看到属于 “chapters.configuration” 组件中任何的 DEBUG 信息。


Example

:sample2.xml

<configuration>
	<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="chapters.configuration" level="INFO" />

	<root level="DEBUG">
		<appender-ref ref="STDOUT" />
	</root>
</configuration>

运行

MyApp3

,可以看到如下的输出信息:

21:52:48.726 [main] INFO  chapters.configuration.MyApp3 - Entering application
21:52:48.728 [main] INFO  chapters.configuration.MyApp3 - Exiting application

可以看到,“chapters.configuration.Foo” 类中的 debug 信息没有被输出。

你可以配置任何 logger 的日志级别。在下一个例子中,我们设置

chapters.configurations

的 logger 日志级别为 INFO,同时设置

chapters.configuration.Foo

的 logger 日志级别为 DEBUG。


Example

:sample3.xml

<configuration>
	<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="chapters.configuration" level="INFO" />
	
	<logger name="chapters.configuration.Foo" level="DEBUG" />
	
	<root level="DEBUG">
		<appender-ref ref="STDOUT" />
	</root>
</configuration>

运行

MyApp3

可以看到如下的输出信息:

22:06:43.500 [main] INFO  chapters.configuration.MyApp3 - Entering application
22:06:43.502 [main] DEBUG chapters.configuration.Foo - Did it again!
22:06:43.502 [main] INFO  chapters.configuration.MyApp3 - Exiting application



3.1.6 ConsoleAppender


ConsoleAppender

就跟名字显示的一样,是将日志事件附加到控制台,跟进一步说就是通过

System.out

或者

System.err

来进行输出。默认通过前者。

ConsoleAppender

通过用户指定的 encoder,格式化日志事件。

属性名 类型 描述
encoder
Encoder
见3.1.4
target String
System.out



System.err

。默认为

System.out


Example: ConsoleAppender configuraion (logback-Console.xml)

<configuration>
	
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" >
		<!-- encoder 默认使用 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
		<encoder>
			<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
		</encoder>	
	</appender>
	
	<root level="DEBUG">
		<appender-ref ref="STDOUT" />
	</root>
</cofiguration>通过以下命令执行上面的配置文件:

修改”chapters.appenders.ConfigurationTester“,把加载的配置文件改为”logback-Console.xml“

String path = System.getProperty("user.dir") + "\\src\\main\\resources\\logback-Console.xml";

运行”chapters.appenders.ConfigurationTester“



3.1.7 FileAppender


FileAppender



OutputStreamAppender

的子类,将日志事件输出到文件中。通过

file

来指定目标文件。如果该文件存在,根据

append

的值,要么将日志追加到文件中,要么该文件被截断。

属性名 类型 描述
append boolean 如果为

true

,日志事件会被追加到文件中,否则的话,文件会被截断。默认为

true
encoder Encoder 参见3.1.4 的属性
file String 要写入文件的名称。如果文件不存在,则新建。在 windows 平台上,用户经常忘记对反斜杠进行转义。例如,

c:\temp\test.log

不会被正确解析,因为

‘\t’

是一个转义字符,会被解析为一个

tab

字符 (\u0009)。正确的值应该像:

c:/temp/test.log

或者

c:\temp\test.log

。没有默认值。
prudent boolean 在严格模式下,

FileAppender

会将日志安全的写入指定文件。即使在不同的 JVM 或者不同的主机上运行

FileAppender

实例。默认的值为

false

。 严格模式可以与

RollingFileAppender

结合使用。 严格模式也意味着

append

属性被自动设置为

true

。 严格模式依赖排他文件锁。实验证明,文件锁大概是写入日志事件成本的 3 倍。在严格模式关闭的情况下,往一台”普通”电脑的硬盘上将一个日志事件写入文件,大概需要耗费 10 微秒。但是在开启的情况下,大概需要 30 微秒。也就是说在关闭的情况下可以一秒钟写入 100’000 个日志事件,但是在开启的情况下,一秒钟只能写入33’000 个日志事件。 严格模式可以在所有 JVM 写入同一个文件时,有效的序列化 I/O 操作。因此,随着竞相访问同一个文件的 JVM 数量上升,将会延迟每一个 I/O 操作。只要总共的 I/O 操作大约为每秒 20 个日志请求,对性能的影响可以被忽略。但是,如果应用每秒产生了 100 个以上的 I/O 操作,性能会受到明显的影响,应该避免使用严格模式。

网络文件锁

当日志文件位于网络文件系统上时,严谨模式的成本会更高。同样重要的是,网络文件系统的文件锁带有很强的偏向性,当前获得锁的进程在释放锁之后会立马又重新获得。因此,当一个进程独占日志文件,将会导致其它进程饥饿死锁。 严格模式的影响严重依赖网速以及操作系统实现的细节。我们提供了一个小型应用FileLockSimulator用于在你的环境中模拟严格模式。


立即刷新

默认情况下,每一个日志事件都会被立即刷新到底层的输出流。默认方法更加的安全,因为日志事件在你的应用没有正确关闭 appender 的情况下不会丢失。但是,要想显著的增加日志的吞吐率,你可以将

immediateFlush

设置为

false

下面是

FileAppender

的配置示例:

Example: logback-fileAppender.xml

<configuration>
	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
		<file>testFile.log</file>
        <!--将 immediateFlush 设置为 false 可以获得更高的日志吞吐量 -->
		<immediateFlush>true</immediateFlush>
        <!--默认为 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
		<encoder>
			<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
		</encoder>
	</appender>
	
	<root level="DEBUG">
		<appender-ref ref="FILE" />
	</root>
</configuration>

修改”chapters.appenders.ConfigurationTester“,把加载的配置文件改为”logback-fileAppender.xml“

String path = System.getProperty("user.dir") + "\\src\\main\\resources\\logback-fileAppender.xml";

运行”chapters.appenders.ConfigurationTester“,运行完成后,工程下生成文件”testFile.log“,如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W41KJILl-1581304468720)(mdpic/log3.png)]

文件内容如下:

206  [main] DEBUG c.appenders.ConfigurationTester - **Hello test 123
208  [main] DEBUG c.appenders.ConfigurationTester - logging statement 0
309  [main] DEBUG c.appenders.ConfigurationTester - logging statement 1
410  [main] DEBUG c.appenders.ConfigurationTester - logging statement 2
510  [main] DEBUG c.appenders.ConfigurationTester - logging statement 3
611  [main] DEBUG c.appenders.ConfigurationTester - logging statement 4
712  [main] DEBUG c.appenders.ConfigurationTester - logging statement 5
813  [main] DEBUG c.appenders.ConfigurationTester - logging statement 6
913  [main] DEBUG c.appenders.ConfigurationTester - logging statement 7
1014 [main] DEBUG c.appenders.ConfigurationTester - logging statement 8
1123 [main] DEBUG c.appenders.ConfigurationTester - logging statement 9
1227 [main] ERROR chapters.appenders.sub.sample.Bar - error-level request
java.lang.Exception: test exception
   at chapters.appenders.sub.sample.Bar.subMethodToCreateRuquest(Bar.java:20)
   at chapters.appenders.sub.sample.Bar.createLoggingRequest(Bar.java:15)
   at chapters.appenders.ConfigurationTester.main(ConfigurationTester.java:35)



3.1.8 文件唯一命名 (使用时间戳)

在应用的开发阶段或者短期应用中,例如:批处理程序,在每个应用启动的时候创建一个新的日志文件。通过

<timestamp>

元素可以轻易做到这点。

Example: logback-timestamp.xml

<configuration>
    <!-- 通过 "bySecond" 将时间格式化成 "yyyyMMdd'T'HHmmss" 的形式插入到 logger 的上下文中 
		这个值对后续的配置也适用
     -->
	<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" />
	
	<appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <!-- 利用之前创建的 timestamp 来创建唯一的文件 -->
		<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

*。

key

属性的值是来区分哪个 timestamp 元素,并且在后续的配置中可以通过[TODO 变量替换]来使用。

datePattern

属性用于将当前时间格式化成字符串。日期格式必须遵循 [SimpleDateFormat]中的规范。

来测试

<timestamp>

元素:

修改”chapters.appenders.ConfigurationTester“,把加载的配置文件改为”logback-timestamp.xml“

String path = System.getProperty("user.dir") + "\\src\\main\\resources\\logback-timestamp.xml";

运行”chapters.appenders.ConfigurationTester“,运行完成后,工程下生成文件”log-+“当前时间”.log“。



3.1.9 RollingFileAppender

[

RollingFileAppender

]继承自

FileAppender

,具有轮转日志文件的功能。例如,

RollingFileAppender

将日志输出到

log.txt

文件,在满足了特定的条件之后,将日志输出到另外一个文件。



RollingFileAppender

进行交互的有两个重要的子组件。第一个是

RollingPolicy

,它负责日志轮转的功能。另一个是

TriggeringPolicy

,它负责日志轮转的时机。所以

RollingPolicy

负责发生什么,

TriggeringPolicy

负责什么时候发生。

为了让

RollingFileAppender

生效,必须同时设置

RollingPolicy



TriggeringPolicy

。但是,如果

RollingPolicy

也实现了

TriggeringPolicy

接口,那么只需要设置前一个就好了。


RollingFileAppender

的属性如下所示:

属性名 类型 描述
file String 参见

FileAppender
append boolean 参见

FileAppender
encoder Encoder 参见

3.1.4
rollingPolicy RollingPolicy 当轮转发生时,指定

RollingFileAppender

的行为。下面将会详细说明
triggeringPolicy TriggeringPolicy 告诉

RollingFileAppender

什么时候发生轮转行为。下面将会详细说明
prudent boolean
FixedWindowRollingPolicy

不支持该属性。

RollingFileAppender

在使用严格模式时要与 [

TimeBasedRollingPolicy](#TimeBasedRollingPolicy) 结合使用,但是有两个限制:<br />1. 在严格模式下,也不支持也不允许文件压缩(我们不能让一个 JVM 在写入文件时,另一个 JVM 在压缩该文件)<br />2. 不能对

FileAppender



file

属性进行设置。实际上,大多数的操作系统不允许在有进程操作文件的情况下对文件改名。<br />其它的参考

FileAppender`



3.1.10 Rolling policy 简介

日志文件的滚动策略,与RollingFileAppender搭配使用,当日志文件发生变动时决定RollingFileAppender的行为;在

<rollingPolicy>

节点中有一个class属性,可选的值为TimeBasedRollingPolicy、FixedWindowRollingPolicy、TriggeringPolicy.



3.1.10.1 TimeBasedRollingPolicy

[

TimeBasedRollingPolicy

]是最常用的轮转策略。它是基于时间来定义轮转策略。例如按天或者按月。

TimeBasedRollingPolicy

既负责轮转的行为,也负责触发轮转。实际上,

TimeBasedRollingPolicy

同时实现了

RollingPolicy



TriggeringPolicy

接口。


TimeBasedRollingPolicy

的配置需要一个强制的属性

fileNamePattern

以及其它的可选属性。

属性名 类型 描述
fileNamePattern String 该属性定义了轮转时的属性名。它的值应该由文件名加上一个

%d

的占位符。

%d

应该包含 java.text.SimpleDateFormat 中规定的日期格式。如果省略掉这个日期格式,那么就默认为

yyyy-MM-dd

。轮转周期是通过

fileNamePattern

推断出来的。 注意:可以选择对

RollingFileAppender



TimeBasedRollingPolicy

的父类)中的

file

属性进行设置,也可以忽略。通过设置

FileAppender



file

属性,你可以将当前活动日志的路径与归档日志的路径分隔开来。当前日志永远会是通过

file

指定的文件。它的名字不会随着时间的推移而发生变化。但是,如果你选择忽略

file

属性,当前活动日志在每个周期内将会根据

fileNamePattern

的值变化。稍后的例子将会说明这一点。

%d{}

中的日期格式将会遵循 java.text.SimpleDateFormat 中的约定。斜杆 ‘/’ 或者反斜杠 ‘’ 都会被解析成目录分隔符。

指定多个 %d

可以指定多个 %d,但是只能有一个是主要的,用于推断轮转周期。其它的 %d 占位符必须通过 ‘aux’ 标记为辅助的。见下面的示例: 多个 %d 占位符允许你在文件夹中去管理归档文件,这个跟轮转周期不同。如下所示:通过年月来管理日志文件夹,但是轮转周期是在每天晚上零点。 /var/log/%d{yyyy/MM, aux}/myapplication.%d{yyyy-MM-dd}.log

TimeZone

在某些情况下,你可能想要根据时区而不是主机的时钟来轮转日志。你可以通过如下方式来指定一个时区,例如: aFloder/test.%d{yyyy-MM-dd-HH, UTC}.log 如果指定的 timezone 不能被识别或者拼写错误,将会根据[TimeZone.getTimeZone(String)]方法指定为 GMT。
maxHistory int 这个可选的属性用来控制最多保留多少数量的归档文件,将会异步删除旧的文件。比如,你指定按月轮转,指定 maxHistory = 6,那么 6 个月内的归档文件将会保留在文件夹内,大于 6 个月的将会被删除。注意:当旧的归档文件被移除时,当初用来保存这些日志归档文件的文件夹也会在适当的时候被移除。
totalSizeCap int 这个可选属性用来控制所有归档文件总的大小。当达到这个大小后,旧的归档文件将会被异步的删除。使用这个属性时还需要设置 maxHistory 属性。而且,maxHistory 将会被作为第一条件,该属性作为第二条件。
cleanHistoryOnStart boolean 如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为 false。 归档文件的删除通常在轮转期间执行。但是,有些应用的存活时间可能等不到轮转触发。对于这种短期应用,可以通过设置该属性为 true,在 appender 启动的时候执行删除操作。

下面是关于

fileNamePattern

的介绍。

fileNamePattern 轮转周期 示例

/wombat/foo.%d
每天轮转(晚上零点)。由于省略了指定 %d 的日期格式,所以默认为

yyyy-MM-dd
没有设置

file

属性:在 2006.11.23 这一天的日志都会输出到

/wombat/foo.2006-11-23

这个文件。晚上零点以后,日志将会输出到

wombat/foo.2016-11-24

这个文件。 设置

file

的值为

/wombat/foo.txt

:在 2016.11.23 这一天的日志将会输出到

/wombat/foo.txt

这个文件。在晚上零点的时候,

foo.txt

将会被改名为

/wombat/foo.2016-11-23

。然后将创建一个新的

foo.txt

,11.24 号这一天的日志将会输出到这个新的文件中。

/wombat/%d{yyyy/MM}/foo.txt
每个月开始的时候轮转 没有设置

file

属性:在 2016.10 这一个月中的日志将会输出到

/wombat/2006/10/foo.txt

。在 10.31 晚上凌晨以后,11 月份的日志将会被输出到

/wombat/2006/11/foo.txt

。 设置

file

的值为

/wombat/foo.txt

:在 2016.10,这个月份的日志都会输出到

/wombat/foo.txt

。在 10.31 晚上零点的时候,

/wombat/foo.txt

将会被重命名为

/wombat/2006/10/foo.txt

,并会创建一个新的文件

/wombat/foo.txt

,11 月份的日志将会输出到这个文件。依此类推。

/wombat/foo.%d{yyyy-ww}.log
每周的第一天(取决于时区) 每次轮转发生在每周的第一天,其它的跟上一个例子类似

/wombat/foo%d{yyyy-MM-dd_HH}.log
每小时轮转 跟之前的例子类似

/wombat/foo%d{yyyy-MM-dd_HH-mm}.log
每分钟轮转 跟之前的例子类似

/wombat/foo%d{yyyy-MM-dd_HH-mm, UTC}.log
每分钟轮转 跟之前的例子类似,不过时间格式是 UTC

/foo/%d{yyyy-MM, aux}/%d.log
每天轮转。归档文件在包含年月的文件夹下 第一个 %d 被辅助标记。第二个 %d 为主要标记,但是日期格式省略了。因此,轮转周期为每天(由第二个 %d 控制),文件夹的名字依赖年与月。例如,在 2016.11 的时候,所有的归档文件都会在

/foo/2006-11/

文件夹下,如:

/foo/2006-11/2006-11-14.log

任何斜杆或者反斜杠够会被当作文件夹分隔符。任何必要的文件夹都会在有需要的时候创建。你可以轻松的将日志文件放在单独的文件夹中。


TimeBasedRollingPolicy

支持文件自动压缩。如果

fileNamePattern



.gz

或者

.zip

结尾,将会启动这个特性。

fileNamePattern 轮转周期 示例

/wombat/foo.%d.gz
每天轮转(晚上零点),自动将归档文件压缩成 GZIP 格式
file

属性没有设置:在 2009.11.23,日志将会被输出到

/wombat/foo.2009-11-23

这个文件。但是,在晚上零点的时候,文件将会被压缩成

/wombat/foo.2009-11-23.gz

。在 11.24,这一天的日志将会被直接输出到

/wombat/folder/foo.2009-11-24

这个文件。

file

属性的值设置为

/wombat/foo.txt

:在 2009.11.23,日志将会被输出到

/wombat/foo.txt

这个文件。在晚上零点的时候,该文件会被压缩成

/wombat/foo.2009-11-23.gz

。并会创建一个新的

/wombat/foo.txt

文件,11.24 这一天的日志将会被输出到该文件。依此类推。


fileNamePattern

有两个目的。logback 通过该属性可以进行周期性的轮转并且得到每个归档文件的名字。注意,两种跟不同的 pattern 可能会有相同的轮转周期。

yyyy-MM



yyyy@MM

同样都是按月轮转,但是归档文件最终的名字不一样。

通过设置

file

属性,你可以将活动日志文件的路径与归档文件的路径分隔开来。日志将会一直输出到通过

file

属性指定的文件中,并且不会随着时间而改变。但是,如果你选择忽略

file

属性,活动日志的名字将会根据

fileNamePattern

的值在每个周期内变化。不设置

file

属性的时候,如果在轮转期间存在外部文件句柄引用日志文件,将会避免[命名错误]。


maxHistory

控制归档文件保留的最大数目,并删除旧的文件。例如,如果你指定按月轮转,并设定

maxHistory

的值为 6,那么 6 个月之内的归档文件都会被保留,大于 6 个月的文件将会被删除。注意,当旧的文件被移除时,为文件归档而创建的文件夹在适当的时候也会被移除。

由于各种技术原因,轮转并不是时间驱动的,而是依赖日志事件。例如,在 2002.03.08,假设

fileNamePattern

的值为

yyyy-MM-dd

(按天轮转),在晚上零点之后,没有日志事件到来,假设在 23 分 47 秒之后,第一个到达的日志事件将会触发轮转。也就是说轮转实际发生在 03.09 00:23’47 AM 而不是 0:00 AM。因此,依赖日志事件的到达速度,所以轮转可能会有延迟。但是,不管延迟的情况是什么样,一定周期内生成的日志事件将会被输出到指定的文件中,从这个角度来看,轮转算法始终都会是正确的。

下面是

RollingFileAppender



TimeBaseRollingPolicy

结合使用的例子:


Example: logback-RollingTimeBased.xml

<configuration>
	<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>logFile.log</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--按天轮转 -->
			<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--保存 30 天的历史记录,最大大小为 30GB -->
			<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>

修改”chapters.appenders.ConfigurationTester“,把加载的配置文件改为”logback-RollingTimeBased.xml“

String path = System.getProperty("user.dir") + "\\src\\main\\resources\\logback-RollingTimeBased.xml";

运行”chapters.appenders.ConfigurationTester“,运行完成后,修改系统时间到下一天,再运行一次,工程下生成了两个文件,一个是今天 的,一个是明天的,安时间来生成文件,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zj2huX8a-1581304468721)(mdpic/1565688203067.png)]



3.1.10.2 基于大小以及时间的轮转策略

有时你希望按时轮转,但同时又想限制每个日志文件的大小。特别是如果后期处理工具需要对日志进行大小限制。为了满足这个需求,logback 配备了

SizeAndTimeBasedRollingPolicy

注意,

TimeBasedRollingPolicy

可以限制归档文件总的大小。所以如果你想要这个限制,你可以通过设置

totalSizeCap

来达到这个目的。

下面的示例展示了基于时间及大小的配置:


Example: logback-sizeAndTime.xml

<configuration debug="true">
	<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>mylog.txt</file>
		<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 按天轮转 -->
			<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
			<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>

注意,除了 %d 之外还有 %i。这两个占位符都是强制要求的。在当前时间还没有到达周期轮转之前,日志文件达到了

maxFileSize

指定的大小,会进行归档,递增索引从 0 开始。

基于大小与时间的文件归档支持删除旧的归档文件。你需要指定

maxHistory

属性的值来保存几个周期的日志。当你的应用停止或者启动的时候,日志将会继续向正确的位置输出。即当前周期内索引最大的。

我们把配置改小一些,看测试效果:

<maxFileSize>3MB</maxFileSize>
<maxHistory>5</maxHistory>
<totalSizeCap>15MB</totalSizeCap>

运行

chapters.appenders.SizeAndTime

,结果如下:

在这里插入图片描述

不用删除日志,再执行一下程序,日志文件不增加,还是5个,如下图所示:

在这里插入图片描述



3.1.10.3 FixedWindowRollingPolicy

在轮转时,[

FixedWindowRollingPolicy

]根据固定窗口算法重命名文件,具体描述如下:


filaNamePattern

表示归档文件的名字。这个属性是必须的,而且必须包含一个表示整形的占位符

i%


FixedWindowRollingPolicy

的可用属性如下:

属性名 类型 描述
minIndex int 表示窗口索引的下界
maxIndex int 表示窗口索引的上界
fileNamePattern String
FixedWindowRollingPolicy

在重命名日志文件时将会根据这个属性来命名。它必须包含一个

i%

的占位符,该占位符指明了窗口索引的值应该插入的位置。 例如,当该属性的值为

MyLogFile%i.log

,最小与最大的值分别为

1



3

。将会产生的归档文件为

MyLogFile1.log



MyLogFile2.log



MyLogFile3.log

。 文件压缩的方式也是通过该属性来指定。例如,设置该属性的值为

MyLogFile%i.log.zip

,那么归档文件将会被压缩成

zip

格式。也可以选择压缩成

gz

格式。

由于窗口固定算法需要跟窗口大小一样的的重命名次数,因此强烈不推荐太大的窗口大小。当用户指定一个较大值时,当前的实现会将窗口大小自动减少为 20。

让我们通过一个例子来了解下固定窗口算法。假设

minIndex

的值为

1



maxIndex

的值为

3



fileNamePattern

的值为

foo%i.log



file

属性的值为

foo.log

轮转数目 当前输出文件 归档日志文件 描述
0 foo.log 还没有到轮转周期,logbak 将日志输出初始文件
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

文件,其它文件的重命名操作跟之前的步骤一样。在本次以及以后的轮转中,将会一直只有三个归档文件以及一个活跃的日志文件

下面的给出了

RollingFileAppender

配合

FixedWindowRollingPolicy

使用的例子。注意,

file

属性是强制的,即使它包含了一些跟

fileNamePattern

属性相同的信息。

Example:logback-RollingFixedWindow.xml

<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>

运行

chapters.appenders.RollingFixedWindow

,可以看到如下的输出信息:

在这里插入图片描述



第四章 SpringBoot使用slf4j



代码工程 :logback-slf4j

springboot项目和spring的项目的使用有点不同,因为springboot把一些依赖自动引入了,所以spring的项目多引几个依赖。

springboot项目只需要引这一个就可以了

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
    <scope>provided</scope>
</dependency>

示例代码,默认日志级别为info,测试的时候DEBUG信息看不到:

代码: com.qianfeng.slf4j.test.Slf4jlTest

package com.qianfeng.slf4j.test;

import com.qianfeng.slf4j.SpringBootMain;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootMain.class)
@WebAppConfiguration
@Slf4j
public class Slf4jlTest {
    @Test
    public void platfromIdTest() {
        log.debug("debug>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        log.info("info>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        log.warn("warn>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        log.error("error>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    }
}



第五章 SpringBoot使用logback



代码工程:

https://github.com/chutianmen/Slf4j-Logback-samples/logback-examples/springboot-logback



4.1 springboot配置

springboot的pom文件都会引一个parent

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.0.3.RELEASE</version>
</parent>

点进去这个parent,会有一个这个dependency

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

再点进去就是2.0.3版本,所谓的它给你集成的各种包依赖,而且规定了版本号,其中有一个包如下

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-logging</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

再点,出来这些都是原有的日志包,所以,不必再引依赖了,直接用就ok了

<dependencies>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-to-slf4j</artifactId>
    <version>2.10.0</version>
    <scope>compile</scope>
  </dependency>
  <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jul-to-slf4j</artifactId>
    <version>1.7.25</version>
    <scope>compile</scope>
  </dependency>
</dependencies>

首先,官方推荐使用的xml名字的格式为:logback-spring.xml而不是logback.xml,至于为什么,因为带spring后缀的可以使用

<springProfile>

这个标签

这样我们就可以使用了:

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  

public class LogbackTest {  
    // 首先获得日志记录这个对象  
   private static Logger logger = LoggerFactory.getLogger(Slf4jTest.class);  
    public static void main(String[] args) {  
        // 记录error信息  
        logger.error("[info message]");  
        // 记录info,还可以传入参数  
        logger.info("[info message]{},{},{},{}", "abc", false, 123,  
                new Slf4jTest());  
        // 记录deubg信息  
        logger.debug("[debug message]");  
        // 记录trace信息  
        logger.trace("[trace message]");  
        System.out.println("hello world");  
    }
}    



4.2 springProfile实现Logback多环境的通用配置



4.2.1 pom配置

在一个基于Spring boot开发的项目里,常常需要有多套环境的配置:本地测试、开发以及产品,本地测试为了查BUG用DEBUG级别,开发使用INFO日志级别,线上产品使用WARN级别。这里给出一个logback的通用配置。在src/main/resources目录下创建配置文件logback-spring.xml,会根据spring.profile.active配置来选择哪个日志输出配置, 如果匹配不上, 则使用第一个日志输出配置,多环境的通用配置内容如下:

Maven Profile设置

<!--环境配置-->
<profiles>
    <!--本地服务器-->
    <profile>
        <id>local</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <profileActive>local</profileActive>
        </properties>
    </profile>

    <!--开发服务器-->
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>false</activeByDefault>
        </activation>
        <properties>
            <profileActive>dev</profileActive>
        </properties>
    </profile>

    <!--线上服务器-->
    <profile>
        <id>prod</id>
        <activation>
            <activeByDefault>false</activeByDefault>
        </activation>
        <properties>
            <profileActive>prod</profileActive>
        </properties>
    </profile>
</profiles>



4.2.2 application.properties

Spring配置Profile与日志输出路径,在application.properties默认配置文件中配置:

spring.profiles.active=@profileActive@
logging.path=D:/log/

为了帮助定制,一些属性从Spring Environment转移到System属性,比如

logging.path

对应

logback-spring.xml

中的

LOG_PATH

,对照表如下:

Spring Environment System Property Comments

logging.exception-conversion-word

LOG_EXCEPTION_CONVERSION_WORD
The conversion word that’s used when logging exceptions.

logging.file

LOG_FILE
Used in default log configuration if defined.

logging.path

LOG_PATH
Used in default log configuration if defined.

logging.pattern.console

CONSOLE_LOG_PATTERN
The log pattern to use on the console (stdout). (Only supported with the default logback setup.)

logging.pattern.file

FILE_LOG_PATTERN
The log pattern to use in a file (if LOG_FILE enabled). (Only supported with the default logback setup.)

logging.pattern.level

LOG_LEVEL_PATTERN
The format to use to render the log level (default

%5p

). (Only supported with the default logback setup.)

PID

PID
The current process ID (discovered if possible and when not already defined as an OS environment variable).



4.2.3 logback-spring.xml

我们根据不同的环境设置不同的日志输出级别:

local(本地开发环境)级别为:debug
dev(测试环境)级别为:info
prod(线上环境)级别为:warn

在resource下创建logback-spring.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="6000000" debug="false">

    <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} %-5p [%t:%c{1}:%L] - %msg%n"/>

    <!-- 系统级配置文件 开始 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>${LOG_PATTERN}</Pattern>
        </layout>
    </appender>

    <!-- stdout -->
    <appender name="rootstdout"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>${LOG_PATH}rootstdout.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <FileNamePattern>${LOG_PATH}rootstdout.%i.log.zip</FileNamePattern>
            <MinIndex>1</MinIndex>
            <MaxIndex>20</MaxIndex>
        </rollingPolicy>
        <triggeringPolicy
                class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>${LOG_PATTERN}</Pattern>
        </layout>
    </appender>


    <springProfile name="local">
        <root level="debug">
            <!-- 本地测试时使用,将日志打印到控制台,实际部署时请注释掉 -->
            <appender-ref ref="STDOUT" />
            <appender-ref ref="rootstdout" />
        </root>
    </springProfile>

    <springProfile name="dev">
        <root level="info">
            <!-- 本地测试时使用,将日志打印到控制台,实际部署时请注释掉 -->
            <appender-ref ref="STDOUT" />
            <appender-ref ref="rootstdout" />
        </root>
    </springProfile>

    <springProfile name="prod">
        <root level="warn">
            <!-- 本地测试时使用,将日志打印到控制台,实际部署时请注释掉 -->
            <appender-ref ref="STDOUT" />
            <appender-ref ref="rootstdout" />
        </root>
    </springProfile>
</configuration>

分别勾选idea—->maven—–>prlfiles下的”local”,“dev”,”prod”三种环境,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZDeySRJ-1581304468724)(mdpic/1571818763719.png)]

根据不同的环境分别运行测试代码:com.qianfeng.logback.test.LogBacklTest :

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootMain.class)
public class LogBacklTest {
    private final static Logger log = LoggerFactory.getLogger(LogBacklTest.class);

    @Test
    public void logbackTest() {
        log.debug("DEBUG级别日志输出。。。。。。");
        log.info("INFO级别日志输出。。。。。。");
        log.warn("WARN级别日志输出。。。。。。");
    }
}

效果如下:

local(本地开发环境)debug级别日志输出:

16:25:01.753 DEBUG [main:c.q.l.t.LogBacklTest:19] - DEBUG级别日志输出。。。。。。
16:25:01.753 INFO  [main:c.q.l.t.LogBacklTest:20] - INFO级别日志输出。。。。。。
16:25:01.753 WARN  [main:c.q.l.t.LogBacklTest:21] - WARN级别日志输出。。。。。。

dev(测试环境)info级别日志输出:

16:27:36.873 INFO  [main:c.q.l.t.LogBacklTest:20] - INFO级别日志输出。。。。。。
16:27:36.874 WARN  [main:c.q.l.t.LogBacklTest:21] - WARN级别日志输出。。。。。。

prod(线上环境)warn级日志输出:

16:28:17.812 WARN  [main:c.q.l.t.LogBacklTest:21] - WARN级别日志输出。。。。。。



第六章 实习题

在springboot开发环境中模拟创建一个logback-spring.xml的XML配置文件,写出模拟测试代码,满足如下要求:

1 日志输出环境分为三种:local(本地环境),dev(开发测试环境),prod(线上环境)
2 local的日志输出级别为debug,dev的日志输出级别为info,prod的输出级别为warn
3 只有在local环境才将日志输出到控制台
4 输出全量日志文件“rootstdout.log”,每个文件50M,历史文件压缩成zip,最多输出20个,超出20个后滚动输出
5 输出debug级别的日志文件“root-debug.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
6 输出info级别的日志文件“root-info.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
7 输出warn级别的日志文件“root-warn.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
8 输出warn级别的日志文件“root-warn.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
9 输出error级别的日志文件“root-error.log”,每天一个文件,最多输出30个,超出删除
10 模拟接口接收外面的信息,单纯输出到一个文件,一天一个文件,历史文件压缩为zip
11 日志的“pattern为“%date{HH:mm:ss.SSS} %-5p [%t:%c{1}:%L] - %msg%n”

输出效果,参考下图所示:

)debug级别日志输出:

16:25:01.753 DEBUG [main:c.q.l.t.LogBacklTest:19] - DEBUG级别日志输出。。。。。。
16:25:01.753 INFO  [main:c.q.l.t.LogBacklTest:20] - INFO级别日志输出。。。。。。
16:25:01.753 WARN  [main:c.q.l.t.LogBacklTest:21] - WARN级别日志输出。。。。。。

dev(测试环境)info级别日志输出:

16:27:36.873 INFO  [main:c.q.l.t.LogBacklTest:20] - INFO级别日志输出。。。。。。
16:27:36.874 WARN  [main:c.q.l.t.LogBacklTest:21] - WARN级别日志输出。。。。。。

prod(线上环境)warn级日志输出:

16:28:17.812 WARN  [main:c.q.l.t.LogBacklTest:21] - WARN级别日志输出。。。。。。



第六章 实习题

在springboot开发环境中模拟创建一个logback-spring.xml的XML配置文件,写出模拟测试代码,满足如下要求:

1 日志输出环境分为三种:local(本地环境),dev(开发测试环境),prod(线上环境)
2 local的日志输出级别为debug,dev的日志输出级别为info,prod的输出级别为warn
3 只有在local环境才将日志输出到控制台
4 输出全量日志文件“rootstdout.log”,每个文件50M,历史文件压缩成zip,最多输出20个,超出20个后滚动输出
5 输出debug级别的日志文件“root-debug.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
6 输出info级别的日志文件“root-info.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
7 输出warn级别的日志文件“root-warn.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
8 输出warn级别的日志文件“root-warn.log”,每个文件50M,历史文件压缩成zip,最多输出10个,超出10个后滚动输出
9 输出error级别的日志文件“root-error.log”,每天一个文件,最多输出30个,超出删除
10 模拟接口接收外面的信息,单纯输出到一个文件,一天一个文件,历史文件压缩为zip
11 日志的“pattern为“%date{HH:mm:ss.SSS} %-5p [%t:%c{1}:%L] - %msg%n”

输出效果,参考下图所示:

在这里插入图片描述



版权声明:本文为jetty_welcome原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。