概要
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
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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-log4j2</artifactId></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
<?xml version="1.0" encoding="UTF-8"?><!-- status表示log4j2本身的日志信息打印级别,和下面的level,不是一个概念 --><!--TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF--><Configuration status="OFF" monitorInterval="30"><Properties><Property name="LOG_PATTERN">%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex</Property><Property name="LOG_FILE_PATH">/Users/xiele/logs/springboot</Property></Properties><Appenders><Console name="ConsoleAppender" target="SYSTEM_OUT" follow="true"><PatternLayout pattern="${LOG_PATTERN}"/></Console><!-- Rolling File Appender --><RollingFile name="FileAppender" fileName="${LOG_FILE_PATH}/spring-boot-log4j2-demo.log"filePattern="${LOG_FILE_PATH}/spring-boot-log4j2-demo-%d{yyyy-MM-dd}-%i.log"><PatternLayout><Pattern>${LOG_PATTERN}</Pattern></PatternLayout><Filters><!-- 只记录INFO级别日志信息,程序打印的其他信息不会被记录 --><!-- 此level设置的日志级别,是过滤日志文件中打印出的日志信息,和Root的level有所区别 --><ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/></Filters><Policies><SizeBasedTriggeringPolicy size="10MB"/><!-- 每天创建一个日志文件 --><TimeBasedTriggeringPolicy interval="1"/></Policies><!--最大保留10个日志文件--><DefaultRolloverStrategy max="10"/></RollingFile></Appenders><Loggers><!-- 此level设置的日志级别,是过滤项目中输出的日志信息,和ThresholdFilter的level有所区别 --><Root level="info"><AppenderRef ref="ConsoleAppender"/><AppenderRef ref="FileAppender"/></Root></Loggers></Configuration>
原理
spring-boot-starter的主模块里,内置的spring.factories文件里配置了很多Listener.
SpringBoot先通过ApplicationListener发布启动事件,然后再启动Spring容器,也就是说日志的初始化在容器之前。
org.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\org.springframework.boot.context.FileEncodingApplicationListener,\org.springframework.boot.context.config.AnsiOutputApplicationListener,\org.springframework.boot.context.config.ConfigFileApplicationListener,\org.springframework.boot.context.config.DelegatingApplicationListener,\org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.context.logging.LoggingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListenerorg.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener
构造方法中,先把配置的所有ApplicationListener放到SpringBoot#listeners中。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}
然后SpringApplication#run方法中,获取配置的:EventPublishingRunListener
然后执行listeners.starting()
然后会执行:LoggingApplicationListener#onApplicationStartingEvent
private void onApplicationStartingEvent(ApplicationStartingEvent event) {this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());this.loggingSystem.beforeInitialize();}
进一步初始化日志系统。
日志系统通过预定义的一些依赖的类,并放到Map中,然后尝试加载,并决定初始化何种日志系统。典型的策略模式:
static {Map<String, String> systems = new LinkedHashMap<>();systems.put("ch.qos.logback.core.Appender", "org.springframework.boot.logging.logback.LogbackLoggingSystem");systems.put("org.apache.logging.log4j.core.impl.Log4jContextFactory","org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");systems.put("java.util.logging.LogManager", "org.springframework.boot.logging.java.JavaLoggingSystem");SYSTEMS = Collections.unmodifiableMap(systems);}
当前类路径下,排除logback后,引入log4j2,最终初始化Log4J2LoggingSystem。
Log4J2LoggingSystem初始化时,默认查找如下文件:
如果不存在,则在上述文件后追加xxx-spring.xml(json)等。
如果找到则委托给Log4j2做日志系统初始化。
如果配置了logging.config,则直接在Listener中直接初始化
