SpringBoot与日志

一、日志框架分类

目前,日志框架有很多,例如:JUL(java.util.logging)、JCL(Apache Jakarta Commons Logging)、Log4j、Log4j2、LogBack、SLF4J、jboss-logging等等。

日志门面日志实现
JCL(Apache Jakarta Commons Logging)
SLF4J(Simple Logging Facade for Java)
jboss-logging
Log4j
JUL(java.util.logging)
Log4j2
Logback
    日志门面就是日志的抽象层,里面只是定义了日志的规范,日志实现就是来具体实现日志门面的。日志门面中这里重点来介绍一下SLF4J。
1、SLF4J

    slf4j是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现。因为接口并不能独立使用,需要和具体的日志框架实现配合使用(如log4j、logback)。

那么问题来了,我们有了日志的实现,为什么还需要日志门面(日志抽象层)?

    我们都知道使用一个接口实际上使用的是这个接口的实现类,那好了,我只要在程序中使用接口中的API来操作日志,然后导入实现了这个日志接口的日志实现类,程序就可以正常的记录日志;下次我们导入了另一套基于这个接口实现的日志实现类,不用改我们在程序中写的任何日志代码程序还是可以正常的记录日志。原理很简单,日志实现类实现了日志接口的规范。因此这个问题的回答总结为一句话:使用日志框架接口更便于更换为其他的日志框架。

    log4j、logback、log4j2都有SLF4J的具体实现,他们既可以单独使用,也可以结合SLF4J框架使用。

相关的Maven依赖写法:
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>${slf4j.version}</version>
    <scope>test</scope>
</dependency>
2、Log4J

    log4j是apache实现的一个开源日志组件。通过使用log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。然而log4j已经很多年没有更新过了,小项目可以使用,大项目还是算了吧。

相关的Maven依赖写法:
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
    <scope>test</scope>
</dependency>
3、Logback

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

    Logback,一个“可靠、通用、快速而又灵活的Java日志框架”。logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日志系统如log4j或JDK Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。 因此在log4j和logback之间选择的话,我们应该选择更强大的logback。理由如下:

  • 1. logback比log4j要快大约10倍,而且消耗更少的内存。
  • 2. logback-classic模块直接实现了SLF4J的接口,所以我们迁移到logback几乎是零开销的。
  • 3. logback不仅支持xml格式的配置文件,还支持groovy格式的配置文件。相比之下,Groovy风格的配置文件更加直观,简洁。
  • 4. logback-classic能够检测到配置文件的更新,并且自动重新加载配置文件。
  • 5. logback能够优雅的从I/O异常中恢复,从而我们不用重新启动应用程序来恢复logger。
  • 6. logback能够根据配置文件中设置的上限值,自动删除旧的日志文件。
  • 7. logback能够自动压缩日志文件。
  • 8. logback能够在配置文件中加入条件判断(if-then-else)。可以避免不同的开发环境(dev、test、uat…)的配置文件的重复。
  • 9. logback带来更多的filter。
  • 10. logback的stack trace中会包含详细的包信息。
  • 11. logback-access和Jetty、Tomcat集成提供了功能强大的HTTP-access日志。
相关的Maven依赖写法:
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.1.11</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.11</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>1.1.11</version>
</dependency>
4、log4j2

引用官网的一句话Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides many of the improvements available in Logback while fixing some inherent problems in Logback’s architecture.翻译过来就是说:Apache Log4j 2是对Log4j的升级,它比其前身Log4j 1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题。Log4j2的特性:

  • 1. 插件式结构。Log4j 2支持插件式结构。我们可以根据自己的需要自行扩展Log4j2. 我们可以实现自己的appender、logger、filter。
  • 2. 配置文件优化。在配置文件中可以引用属性,还可以直接替代或传递到组件。而且支持json格式的配置文件。不像其他的日志框架,它在重新配置的时候不会丢失之前的日志文件。
  • 3. Java 5的并发性。Log4j 2利用Java 5中的并发特性支持,尽可能地执行最低层次的加锁。解决了在log4j 1.x中存留的死锁的问题。
  • 4. 异步logger。Log4j2是基于LMAX Disruptor库的。在多线程的场景下,和已有的日志框架相比,异步的logger拥有10倍左右的效率提升。
相关的Maven依赖写法:
<dependencies>  
    <dependency>  
        <groupId>org.apache.logging.log4j</groupId>  
        <artifactId>log4j-api</artifactId>  
        <version>{log4j.version}</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.logging.log4j</groupId>  
        <artifactId>log4j-core</artifactId>  
        <version>{log4j.version}</version>  
    </dependency>
    <!-- log4j2异步日志需要加载disruptor-3.0.0.jar或者更高的版本 -->
    <dependency>
        <groupId>com.lmax</groupId>
        <artifactId>disruptor</artifactId>
        <version>3.3.6</version>
    </dependency>
    <!--如果还要使用slf4j可以导入下面的依赖-->  
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.9.1</version>
</dependency>
</dependencies>

二、 日志框架的使用

1、SLF4J
2、什么时候使用SLF4J比较合适呢?

    如果你开发的是类库或者嵌入式组件,那么就应该考虑采用SLF4J,因为不可能影响最终用户选择哪种日志系统。在另一方面,如果是一个简单或者独立的应用,确定只有一种日志系统,那么就没有使用SLF4J的必要。

3、如何在系统中使用SLF4J?(官网:https://www.slf4j.org/manual.html)

下面是官网给的一个使用实例,以后如果要是用SLF4J,那就不要直接调用实现类的API,而是应该调用SLF4J里面的API,当然只是使用日志实现类另当别论!

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

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    //输出一个info级别的日志
    logger.info("Hello World");
  }
}
4、Log4J

    log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、数据库等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
    Log4j有7种不同的log级别,按照等级从低到高依次为:TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF。如果配置为OFF级别,表示关闭log。 Log4j支持两种格式的配置文件:properties和xml。包含三个主要的组件:Logger、appender、Layout。

一个简单的log4j配置文件
### set log levels ###  
log4j.rootLogger = INFO , console , debug , error  
  
### console ###  
log4j.appender.console = org.apache.log4j.ConsoleAppender  
log4j.appender.console.Target = System.out  
log4j.appender.console.layout = org.apache.log4j.PatternLayout  
log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n  
  
### log file ###  
log4j.appender.debug = org.apache.log4j.DailyRollingFileAppender  
log4j.appender.debug.File = xzy_mybatis.log  
log4j.appender.debug.Append = true  
log4j.appender.debug.Threshold = INFO  
log4j.appender.debug.layout = org.apache.log4j.PatternLayout  
log4j.appender.debug.layout.ConversionPattern = %-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n  
  
### exception ###  
log4j.appender.error = org.apache.log4j.DailyRollingFileAppender  
log4j.appender.error.File = xzy_mybatis.log  
log4j.appender.error.Append = true  
log4j.appender.error.Threshold = ERROR  
log4j.appender.error.layout = org.apache.log4j.PatternLayout  
log4j.appender.error.layout.ConversionPattern = %-d{yyyy-MM-dd HH\:mm\:ss} [%p]-[%c] %m%n  

# LOG4J配置
log4j.rootCategory=INFO,stdout,jdbc

# 数据库输出
log4j.appender.jdbc=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.jdbc.driver=com.mysql.jdbc.Driver
log4j.appender.jdbc.URL=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=true
log4j.appender.jdbc.user=root
log4j.appender.jdbc.password=root
log4j.appender.jdbc.sql=insert into log_icecoldmonitor(level,category,thread,time,location,note) values('%p','%c','%t','%d{yyyy-MM-dd HH:mm:ss:SSS}','%l','%m')

在Spring Boot中使用log4j
    首先创建一个Spring Boot工程,在创建Spring Boot工程时,我们引入了spring-boot-starter,其中包含了spring-boot-starter-logging,由于Spring Boot默认的日志框架是Logback,所以我们在引入log4j之前,需要先排除该包的依赖,再引入log4j的依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j</artifactId>
</dependency>

配置log4j-spring.properties
    配置文件的编写方法上面介绍的没有啥区别,但是配置文件的名字可以是log4j-spring.properties也可以是log4j.properties,但是Spring Boot官方推荐的命名方式是第一种方式。配置文件的内容参考上面。
在Spring Boot主配置文件中指定日志文件的配置

loggin.config=classpath:log4j-spring.properties
5、Logback
logblack的加载顺序

SLF4J+Logback是Spring Boot默认的日志策略,logback支持xml和groovy形式的配置文件,而且还支持编程式地配置,它加载配置文件的顺序:

  • 在 classpath 中寻找 logback-test.xml文件
  • 如果找不到 logback-test.xml,则在 classpath 中寻找 logback.groovy 文件
  • 如果找不到 logback.groovy,则在 classpath 中寻找 logback.xml文件
  • 如果上述的文件都找不到,则 logback 会使用 JDK 的 SPI 机制查找 META-INF/services/ch.qos.logback.classic.spi.Configurator 中的 logback 配置实现类,这个实现类必须实现 Configuration 接口,使用它的实现来进行配置
  • 如果上述操作都不成功,logback 就会使用它自带的 BasicConfigurator 来配置,并将日志输出到 console
在Spring Boot中使用LogBack(官网:https://logback.qos.ch/documentation.html)

前面说过,logback是Spring Boot默认的日志系统,假如对日志没有特殊要求,可以完全零配置使用 SLF4J(Simple Logging Facade For Java)的logback来输出日志。

package com.jianeye.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

SpringBootApplication
public class TestApplication {

    private static Logger logger = LoggerFactory.getLogger(TestApplication.class);

    public static void main(String[] args) {
        logger.warn("logback --------------------------------\n");  
        SpringApplication.run(TestApplication.class, args);
        logger.info("default log system *************************\n");  
    }
}

    上面这段程序,不经过任何配置,默认的日志系统是完全可以正常运行的。但是Spring Boot也支持我们修改默认配置,修改的方式有两种,一种是在src/main/resources下新建logback-spring.xml文件(logback.xml也可以,但官方推荐前面的写法);第二种是直接在Spring Boot主配置文件中配置,下面是简单的配置示例:
logbac-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml" />
    <logger name="org.springframework.web" level="INFO"/>
    <logger name="org.springboot.sample" level="TRACE" />

    <springProfile name="dev">
        <logger name="org.springboot.sample" level="DEBUG" />
    </springProfile>

    <springProfile name="staging">
        <logger name="org.springboot.sample" level="INFO" />
    </springProfile>
</configuration>

application.properties

debug=true
server.port=80
server.servlet.context-path=/book

#指定配置文件的名字
#logging.config.name=logback-spring.xml
#配置文件的位置,
#logging.config.location=classpath:logback-spring.xml
#指定输出文件,当不指定路径时,默认将在当前项目路径下创建日志文件。
#logging.file=logback-spring.log
#也可以指定路径到硬盘的具体的某个路径,如果某及路径不存在就会创建
logging.file=G://log/logback-spring.log

#控制台输出的日期格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
#日期输出格式
logging.pattern.dateformat=HH:mm:ss.sss
#指定输出到文件的日志格式
logging.pattern.file=file-%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} -%msg%n
#指定日志中日志级别输出的格式
logging.pattern.level=custom-%5p

#调整根目录级别的日志级别
logging.level.root=WARN
#调整特定文件的日志级别 logging.level.文件名=日志级别
logging.level.com.xust=INFO
logging.level.org.springframework=DEBUG
6、扩展使用springProperty

    在logback-spring.xml中可以使用Spring Boot扩展的,使用它可以把在application.properties中定义的属性值映射为logback环境中的一个属性,从而可以在logback的上下文使用。

<springProfile name="staging">
    <!-- configuration to be enabled when the "staging" profile is active -->
  	<!--可以指定某段配置只在某个环境下生效-->
</springProfile>

举个例子:
    下面的配置中定义了属性appName对应于Spring Boot的Environment中的app.name(由source属性指定),当未指定时默认使用defaultValue属性指定的TEST;属性name对应于Spring Boot的Environment中的logging.path,未指定时使用/logs/${appName}.log,其中的${appName}又对应于变量appName的值。定义好的变量可以在logback的配置文件中以${varName}的形式进行引用。

logback-spring.xml
<configuration>

        <!--
        日志输出格式:
			%d表示日期时间,
			%thread表示线程名,
			%-5level:级别从左显示5个字符宽度
			%logger{50} 表示logger名字最长50个字符,否则按照句点分割。 
			%msg:日志消息,
			%n是换行符
        -->

    <!--默认的-->
    <springProperty name="appName" source="app.name" defaultValue="TEST"/>
    <!---->
    <springProperty name="logPath" source="log.path" defaultValue="/logs/${appName}.log"/>
    
    <appender name="stdOut" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${appName}-%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</pattern>
        </encoder>
    </appender>

    <appender name="fileOut" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${logPath}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${logPath}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</pattern>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="stdOut" />
        <appender-ref ref="fileOut" />
    </root>
</configuration>

三、多个不同的日志框架共用SLF4J

    经常在实际的开发环境中,我们会使用到不同的框架,而且不同的框架默认的日志系统不同,那么如何在Spring Boot中设置一下,让这些不同框架的默认日志系统可以借助于Spring Boot的默认日志系统来工作?这是可以办到的,具体的步骤如下:
1、在pom.xml文件中导入依赖的时候排除所使用框架对默认日志系统的依赖
2、用SLF4J提供的中间包替换原有日志框架
下图有详细的配置方法:

留言区

还能输入500个字符