转载

SpringBoot 与 Logback 日志配置

本文记录 SpringBoot 与 Logback 是如何工作的,即记录 SpringBoot 中 Logback  是怎么一步一步初始化的。用以测试的 SpringBoot 版本是 1.5.16, 而非最新的 SpringBoot 2。关于 SpringBoot 日志的官方文档在 Logging , 但不太详细或透彻。本文不承诺说理解得更有深度,只是为官方文档提供更多方面的参考。

SpringBoot 默认使用 Slf4J + Logback 来记录日志,对于一个基本的依赖于

<dependency>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-starter</artifactId>  </dependency>

的项目,它依赖了 spring-boot-starter-logging 组件,而该组件引入了以下几个依赖

  1. logback-classic:   依赖了 Slf4J
  2. jcl-over-slf4j
  3. jul-to-slf4j
  4. log4j-over-slf4j

相当于把其他的日志框架全桥接到了 Slf4J + Logback 上去了。

那么  SpringBoot Web 项目是怎么样子的呢?spring-boot-starter-web 依赖于  spring-boot-starter,所以日志框架选用上就没有一点区别了。

SpringBoot 应用默认日志输出

从一个最简单的 SpringBoot 应用程序来感受它的配置日志输出,一个 Maven 项目,最基本的配置是

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.16.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
 
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
</dependencies>

注意: spring-boot-starter-parent 的 pom.xml 文件值得瞧一瞧的。

application.properties 文件为空,并且没有任何的 logback 配置文件在 resources 目录中。

来个最简单的程序

@SpringBootApplication
public class Application {
    private static final Logger logger = LoggerFactory.getLogger(Application.class);
 
    public static void main(String[] args) {
        logger.info("aaa");
        SpringApplication.run(Application.class, args);
        logger.info("bbb");
    }
}

在 SpringApplication.run(...) 前后各调用  logger.info(...) 输出信息

SpringBoot 与 Logback 日志配置

前面 logger.info("aaa") 和 logger.info("bbb") 的输出用红线标示出来了,可以非常感性的认识到

  1. 它们输出样式不同,所以使用了不同的日志配置
  2. logger.info("aaa") 发生在 Spring 上下文初始化之前,以 Logback 的默认行为初始化的 LoggerFactory
  3. logger.info("bbb") 发生在  Spring 上下文初始化之后,因此 SpringBoot 又重新配置了 Logback 的 LoggerFactory
  4. #2 不是我们这里探讨的范畴,它和普通 Java 应用使用 Logback 是一致的,主要看 #3 怎么来的,并且默认的 Log 是怎么样的配置

SpringBoot 是如何初始化 Logback 的

在 Logging 一文中提到了 Spring Boot 有一个 LoggingSystem 抽象来负责配置日志系统,并且 Logback 是首选。 LoggingSystem 是一个抽象类,它的实现层次如下

SpringBoot 与 Logback 日志配置 既然说 Logback 是首先,那么 SpringBoot 最终是要用到 LogbackLoggingSystem 这个类的,那我们从源代码跟踪一下 SpringBoot 的 Spring 上下文是如何与 Logback 衔接起来的。

能与 Spring 上下文进行交互的一般来说是 ApplicationEvent, 这里是 org.springframework.boot.logging.LoggingApplicationListener , 它实现了 ApplicationListener , 看 LoggingApplicationListener 的事件方法

@Override
public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationStartingEvent) {
        onApplicationStartingEvent((ApplicationStartingEvent) event);  // #1
    }
    else if (event instanceof ApplicationEnvironmentPreparedEvent) {
        onApplicationEnvironmentPreparedEvent(   // #2
            (ApplicationEnvironmentPreparedEvent) event);
    }
    else if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent((ApplicationPreparedEvent) event);  // #3
    }
......

#1 找到 LoggingSystem 的实现类,在 LoggingSystem.get(classloader) 方法中,如果配置了系统属性 org.springframework.boot.logging.LoggingSystem 对应的实现类的话,就用它,指定为 none 值就用 NoOpLogginSystem 实现,即没有任何日志输出。如果没有指定 org.springframework.boot.logging.LoggingSystem 系统属性, LoggingSystem 则尝试以下的顺序找实现类

  1. ch.qos.logback.core.Appender => org.springframework.boot.logging.logback.LogbackLoggingSystem
  2. ora.apache.logging.log4j.core.impl.Log4jContextFactory => org.springframework.boot.logging.log4j2.Log4J2LoggingSystem
  3. java.util.logging.LogManager => org.springframework.boot.logging.java.JavaLoggingSystem

而显然 LogbackLoggingSystem 对应的类 ch.qos.logback.core.Appender 是存在于 springboot starter 中的,所以在 #1 中可以确定是用 LogbackLoggingSystem 实现

#3 先说这最后一步,如果初始化好,把 LoggingSystem 的实例(此处为 LogbackLoggingSystem 实例) 注册名为 springBootLogginSystem 的 Spring Bean

#2 对日志进行配置,具体实现在 LoggingApplicationListener.initialize(environment, classLoader)LogbackLoggingSystem.initialize(...) 方法中。不列出实际代码来了,只解翻译一下过程

  1. 试图从 Spring 属性(包括配置在 Spring 属性文件,--logging.file= 或 -Dlogging.file= 这样的配置)读出配置的  logging.file  和  logging.path  值,如果有的话,分别映射为 LOG_FILE  和 LOG_PATH  系统属性值,这可以在 logback.xml 的配置中用 ${LOG_FILE} 引用到
  2. 初始日志级别的设置,如果 spring 属性中配置了  debug=true  则为 LogLevel.DEBUG, 如果配置了 trace=true  则为 LogLevel.TRACE。后面还会专为某些包预设一些日志级别,并且最后的日志级别可在 Spring 属性中用 logging.level.logger_name=DEBUG  来配置,如 logging.level.org.springframework=DEBUG
  3. 如果用 Spring 属性 logging.config  指定了配置文件,则使用该配置文件初始化 Logback 的 LoggerFactory,否则 LogbackLoggingSystem  将会以下面的顺序来查找 Logback 配置文件

    logback-test.groovy, logback-test.xml, logback.groovy, logback.xml

    logback-test-spring.xml, logback-test-spring.xml, logback-spring.groovy, logback-spring.xml (AbstractLoggingSystem.getSpringConfigLocations() 方法)

    注意:SpringBoot 会忽略掉普通 Logback 应用的系统属性 logback.configurationFile 设定配置文件的方法

    if (StringUtils.hasText(System.getProperty(CONFIGURATION_FILE_PROPERTY))) {
        getLogger(LogbackLoggingSystem.class.getName()).warn(
            "Ignoring '" + CONFIGURATION_FILE_PROPERTY + "' system property. "
                + "Please use 'logging.config' instead.");
    }
    
  4. 如果检查了以上八个文件都不存在的话,就要调用 LogbackLoggingSystem.loadDefaults(initializationContext, logFile)  来配置默认的日志。

SpringBoot 没有任何日志配置文件时配置

这块其实是上面步骤 #2 中的一个子步骤,因其重要才将其单独列出,来看看 SpringBoot 在没有加载到任何的配置文件时如何配置默认 Logback 的 LoggerFactory。入口就是 LogbackLoggingSystem.loadDefaults(initializationContext, logFile)

首先,默认显示日志级别的格式是: ${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}} , 可用 Spring 属性 logging.pattern.level 或系统属性 LOG_LEVEL_PATTERN ,默认为  %5p

其他的默认配置就要参考类 org.springframework.boot.logging.logback.DefaultLogbackConfiguation

不管有没有配置 logging.filelogging.path ,SpringBoot 都会初始化 consoleAppender, 并且默认的输出模式是

private static final String CONSOLE_LOG_PATTERN = "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} "
        + "%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} "
        + "%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} "
        + "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";

该模式可用 Spring 属性 logging.pattern.console 进行覆盖设置。注意,SpringBoot 还为我们定义了 clr , wEx 这两个 Converter

如果配置了 Spring 属性 logging.filelogging.path 其中一个或两个,就会在 consoleAppender 的基础上再加一个 fileAppender , 它的输出模式是

private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} "
    + "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}";

该模式可以用 Spring 属性 logging.pattern.file 进行覆盖。

日志文件是 10MB 大小不断滚动的,不会删除旧文件。

日志文件路径如何决定的

  1. 如果只设置了 Spring 属性 logging.file , 就是  logging.file  所指定的文件,文件名可以是绝对文件路径,或者相对路径
  2. 如果只设置了 Spring 属性 logging.path , 那么日志文件是 logging.path  下的 spring.log  文件
  3. 如果以上两个属性同时指定了,则只有 logging.file  是有用的,与 #1 同

无论是对于 consoleAppender 还是 fileAppender , 都是设置 INFO 为默认日志级别,并且预设了一些 logger 的日志输出级别。

简单的 SpringBoot 日志文件配置

理解了 SpringBoot 是如何初始化 Logback 日志配置后,我们来看一下项目中几种最简的日志配置方式。

日志同时输出到控制台和文件

依据 SpringBoot 加载 Logback  配置文件的顺序,我们可以在 classpath 下放 logback-spring.xmllogback.xml , 注意是 logback.xml 被优先选择。内容如下

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

设置 Spring 属性 logging.file 或系统属性 LOG_FILE 来指定日志输出文件名。或者用 Spring 属性  logging.path 或系统属性 LOG_PATH 指定 spring.log 的路径。

日志只输出到文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
    <include resource="org/springframework/boot/logging/logback/file-appender.xml" />
    <root level="INFO">
        <appender-ref ref="FILE" />
    </root>
</configuration>

用上面相同的方式指定日志文件的路径。

只输出到控制台

就更简单了,可以什么配置文件也不要,并且不要配置 logging.file 和  logging.path 。而且效果与下面的配置是一样的。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

小结:

  1. SpringBoot 基础的依赖于  starter  的项目默认采用 Slf4J + Logback 来输出日志
  2. 没有任何配置文件,也没有配置 Spring 属性 logging.file , logging.path  或系统属性 LOG_FILELOG_PATH  的情况下只会输出日志到控制台
  3. 没有任何配置文件,且有配置 #2 中任何一个属性的情况下将会同时输出日志到控制台与文件,文件以 10MB 大小滚动,不删除旧文件
  4. 不采用外部配置文件的情况下,可以用一些 Spring 属些来进行简单的日志配置,如 logging.pattern.file  等
  5. 可以设置 Spring 属性 logging.config  来指定外部 logback 配置文件,但忽略 Logback 默认用系统属性 logback.configurationFile  指定配置文件的方式
  6. 以上叙述的 Spring 属性,可用多种设置方式,如 Spring 属性 abc ,有四种方式:1) application.properties 文件中的 abc=xxx , 2)环境变量 export abc=xxx , 3) 启动参数  --abc=xxx ,4) 系统属性  -Dabc=xxx
  7. 类路径下的配置文件可以用 logback-spring.xml  或 logback.xml ,但是 logback.xml  优先加载,并未遵循先特殊再普通的原则。(Logback 1.3.0 之后由于支持 Java 9,但是 Groovy 与 Java 9 未处理好关系,所以 Logback 1.3.0 不能支持 .groovy 的配置文件)
  8. 为更精细化的控制 Logback 的输出,我们通常都会在类路径下旋转 logback.xml  或 logback-spring.xml  配置文件,二选一了。我没发现这两个文件有什么不同。
原文  https://yanbin.blog/springboot-work-with-logback/
正文到此结束
Loading...