概要

SpringBoot内部使用CommonsLogging,并依赖与具体的底层日志库实现。默认支持Java Util Logging,Log4j2, Logback.

控制台输出日志

可以通过命令行指定:java -jar myapp.jar —debug 或在application.properties 配置:

debug=true

注:通过指定debug=true,只会对SpringBoot打印的日志生效,不会对应用自身的日志生效。

文件输出

默认情况下,SpringBoot只会输出日志到控制台,如果要输出到文件,需要在application.properties单独配置:

logging.file.name=demo.log logging.file.path=/Users/xiele/logs/springboot/

日志级别

logging.level.root = info

自定义日志配置

logging.config=单独的日志配置文件路径

logging.config配置可以指定自定义的日志配置。日志系统的加载在Spring容器创建之前,如果未配置logging.config, 根据实际依赖的日志Starter,SpringBoot会加载classpath下如下的文件配置:

Logging System Customization
Logback logback-spring.xml, logback-spring.groovy, logback.xml, or logback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) logging.properties

Logback 扩展

SpringBoot建议,日志命名为:xx-spring.xml
由于logback或log4j2的标准日志会提前加载,无法使用Spring的扩展配置,因此建议命名-spring.xml,这样是让Spring来负责加载日志系统文件,并委托为具体的日志库。

SpringBoot使用Log4j2

排除logback引入Log4j2

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter</artifactId>
  8. <exclusions>
  9. <exclusion>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-logging</artifactId>
  12. </exclusion>
  13. </exclusions>
  14. </dependency>
  15. <dependency>
  16. <groupId>org.springframework.boot</groupId>
  17. <artifactId>spring-boot-starter-log4j2</artifactId>
  18. </dependency>

配置log4j2的XML文件

默认情况下,Spring会扫描classpath下的log4j2.xml,log4j2.json,log4j2.yaml. 可以单独定义配置文件,例如命名为log4j2-spring.xml. 通过logging.config来指定路径

logging.config=classpath:log4j2/log4j2-spring.xml (也可以加入到某个目录中)

如果log4j2-spring.xml在classpath下,默认可被springboot加载。

log4j2-spring.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- status表示log4j2本身的日志信息打印级别,和下面的level,不是一个概念 -->
  3. <!--TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF-->
  4. <Configuration status="OFF" monitorInterval="30">
  5. <Properties>
  6. <Property name="LOG_PATTERN">
  7. %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex
  8. </Property>
  9. <Property name="LOG_FILE_PATH">/Users/xiele/logs/springboot</Property>
  10. </Properties>
  11. <Appenders>
  12. <Console name="ConsoleAppender" target="SYSTEM_OUT" follow="true">
  13. <PatternLayout pattern="${LOG_PATTERN}"/>
  14. </Console>
  15. <!-- Rolling File Appender -->
  16. <RollingFile name="FileAppender" fileName="${LOG_FILE_PATH}/spring-boot-log4j2-demo.log"
  17. filePattern="${LOG_FILE_PATH}/spring-boot-log4j2-demo-%d{yyyy-MM-dd}-%i.log">
  18. <PatternLayout>
  19. <Pattern>${LOG_PATTERN}</Pattern>
  20. </PatternLayout>
  21. <Filters>
  22. <!-- 只记录INFO级别日志信息,程序打印的其他信息不会被记录 -->
  23. <!-- 此level设置的日志级别,是过滤日志文件中打印出的日志信息,和Root的level有所区别 -->
  24. <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/>
  25. </Filters>
  26. <Policies>
  27. <SizeBasedTriggeringPolicy size="10MB"/>
  28. <!-- 每天创建一个日志文件 -->
  29. <TimeBasedTriggeringPolicy interval="1"/>
  30. </Policies>
  31. <!--最大保留10个日志文件-->
  32. <DefaultRolloverStrategy max="10"/>
  33. </RollingFile>
  34. </Appenders>
  35. <Loggers>
  36. <!-- 此level设置的日志级别,是过滤项目中输出的日志信息,和ThresholdFilter的level有所区别 -->
  37. <Root level="info">
  38. <AppenderRef ref="ConsoleAppender"/>
  39. <AppenderRef ref="FileAppender"/>
  40. </Root>
  41. </Loggers>
  42. </Configuration>

原理

spring-boot-starter的主模块里,内置的spring.factories文件里配置了很多Listener.
SpringBoot先通过ApplicationListener发布启动事件,然后再启动Spring容器,也就是说日志的初始化在容器之前。

  1. org.springframework.context.ApplicationListener=\
  2. org.springframework.boot.ClearCachesApplicationListener,\
  3. org.springframework.boot.builder.ParentContextCloserApplicationListener,\
  4. org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
  5. org.springframework.boot.context.FileEncodingApplicationListener,\
  6. org.springframework.boot.context.config.AnsiOutputApplicationListener,\
  7. org.springframework.boot.context.config.ConfigFileApplicationListener,\
  8. org.springframework.boot.context.config.DelegatingApplicationListener,\
  9. org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
  10. org.springframework.boot.context.logging.LoggingApplicationListener,\
  11. org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
  12. org.springframework.boot.SpringApplicationRunListener=\
  13. org.springframework.boot.context.event.EventPublishingRunListener

构造方法中,先把配置的所有ApplicationListener放到SpringBoot#listeners中。

  1. public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  2. this.resourceLoader = resourceLoader;
  3. Assert.notNull(primarySources, "PrimarySources must not be null");
  4. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  5. this.webApplicationType = WebApplicationType.deduceFromClasspath();
  6. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  7. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  8. this.mainApplicationClass = deduceMainApplicationClass();
  9. }

然后SpringApplication#run方法中,获取配置的:EventPublishingRunListener
然后执行listeners.starting()
image.png
然后会执行:LoggingApplicationListener#onApplicationStartingEvent

  1. private void onApplicationStartingEvent(ApplicationStartingEvent event) {
  2. this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
  3. this.loggingSystem.beforeInitialize();
  4. }

进一步初始化日志系统。
日志系统通过预定义的一些依赖的类,并放到Map中,然后尝试加载,并决定初始化何种日志系统。典型的策略模式:

  1. static {
  2. Map<String, String> systems = new LinkedHashMap<>();
  3. systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");
  4. systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory",
  5. "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
  6. systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");
  7. SYSTEMS = Collections.unmodifiableMap(systems);
  8. }

当前类路径下,排除logback后,引入log4j2,最终初始化Log4J2LoggingSystem。
Log4J2LoggingSystem初始化时,默认查找如下文件:
image.png
如果不存在,则在上述文件后追加xxx-spring.xml(json)等。
如果找到则委托给Log4j2做日志系统初始化。

如果配置了logging.config,则直接在Listener中直接初始化
image.png