动态修改springboot日志级别
一、背景
为了减少日志频繁打印带来的性能影响,线上设置的日志级别相对较高。当线上应用出现问题需要我们排查的时候,可能需要适当降低日志级别(例如DEBUG)来打印更多的日志信息。
传统的方式需要1、修改日志级别 2、重启应用 3、问题复现排查问题。这个过程需要重启应用,比较麻烦,效率较低。考虑某一种方式能不重启应用的情况下能够动态修改日志级别。
二、方案
spring boot1.5开始,spring actuator开始支持对日志级别的动态修改。
1、基础配置
actuator 暴露的http接口只有info和health,需要把loggers端点配置上才能通过调用http接口使用日志功能。
2、问题出现
调用接口查询和设置日志级别都没有报错。设置新的日志级别后再查询也是设置后的级别。但是控制台看并没有生效,还是打印启动时设置的日志级别及以上的日志信息。
PS:上面提到的日志设置级别没有生效的原因,是因为现在的产品应用中都是集成的log4j日志框架。这个日志框架比较老,actuator接口已经不支持设置log4j日志级别了。我调用设置级别的接口不报错,是因为是设置级别最终落到了默认的JavaLoggingSystem这个日志框架上了。但是代码中@Slf4j绑定的日志框架是log4j。所以没有生效。
我们的项目中exclude了spring boot默认支持的logback而选用了老版本的log4j。
3、通过源码分析问题原因
这个loggers()就是对外提供的查询日志的接口。我们看到是通过this.loggingSystem这个对象的操作完成日志的查询包括以后的配置。
在应用启动时会调用LoggingSystem中的public static LoggingSystem get(ClassLoader classLoader)方法。这个方法会选择当前应用正在使用的日志实现框架。
我们可以看到,上图看到,会从SYSTEMS这个Map中筛选出当前应用的日志实现框架。当前支持的框架有三个:logback, log4j2LoggingSystem, JavaLoggingSystem。如果没有匹配到配置的日志框架,会默认选中JavaLoggingSystem。
下面的logging包中也可以看到LoggingSystem支持的是java,log4j2和loback这三个日志实现框架
4、方案
集成log4j2框架
compile 'org.springframework.boot:spring-boot-starter-log4j2'
同时exclude下面关于log4j的组件, 否则log4j和log4j2共存的情况下,会默认选择log4j。
all*.exclude group: 'org.slf4j', module: 'slf4j-log4j12'
all*.exclude group: 'log4j', module: 'log4j'
log4j2配置文件模板
为了和原来log4j的打印日志效果相同,在RollingFile标签中配置输出日志文件的名称,格式和滚动时间间隔。另外在<logger>标签中指定com.XX.XX包。
- <?xml version="1.0" encoding="UTF-8"?>
- <!DOCTYPE configuration>
- <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
- <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
- <configuration monitorInterval="5">
- <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
- <!--变量配置-->
- <Properties>
- <!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符-->
- <!-- %logger{36} 表示 Logger 名字最长36个字符 -->
- <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
- <!-- 定义日志存储的路径 -->
- <property name="FILE_PATH" value="log" />
- <property name="FILE_NAME" value="ebs" />
- <property name="PRODUCT_NAME" value="ebs" />
- </Properties>
- <appenders>
- <console name="Console" target="SYSTEM_OUT">
- <!--输出日志的格式-->
- <PatternLayout pattern="${LOG_PATTERN}"/>
- <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
- <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
- </console>
- <!-- <!–文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用–>
- <File name="Filelog" fileName="${FILE_PATH}/test.log" append="false">
- <PatternLayout pattern="${LOG_PATTERN}"/>
- </File>-->
- <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
- <RollingFile name="RollingFileInfo" fileName="${FILE_PATH}/${PRODUCT_NAME}/audit.log" filePattern="${FILE_PATH}/${PRODUCT_NAME}/audit-%d{yyyy-MM-dd-HH}.log">
- <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
- <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
- <PatternLayout pattern="${LOG_PATTERN}"/>
- <Policies>
- <!--interval属性用来指定多久滚动一次,默认是1 hour-->
- <TimeBasedTriggeringPolicy modulate="true" interval="1"/>
- <!--<SizeBasedTriggeringPolicy size="10MB"/>-->
- </Policies>
- <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
- <DefaultRolloverStrategy max="15"/>
- </RollingFile>
- </appenders>
- <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
- <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
- <loggers>
- <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
- <logger name="org.hibernate" level="info" additivity="false">
- <AppenderRef ref="Console"/>
- </logger>
- <!--监控系统信息-->
- <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
- <Logger name="org.springframework" level="info" additivity="false">
- <AppenderRef ref="Console"/>
- </Logger>
- <Logger name="com.XX.XX" level="info" additivity="false">
- <AppenderRef ref="RollingFileInfo" />
- </Logger>
- <root level="info">
- <appender-ref ref="Console"/>
- </root>
- </loggers>
- </configuration>
实现:log4j-api:log4j2定义的API, log4j-core:log4j2上述API的实现
三、测试接口
查询:get http://localhost:8080/loggers
设置:post http://localhost:8080/loggers/{name}
1、查询日志级别
get http://localhost:8080/loggers
- {
- "levels": [
- "OFF",
- "FATAL",
- "ERROR",
- "WARN",
- "INFO",
- "DEBUG",
- "TRACE"
- ],
- "loggers": {
- "ROOT": {
- "configuredLevel": "INFO",
- "effectiveLevel": "INFO"
- },
- "com.XX.XX": {
- "configuredLevel": "WARN",
- "effectiveLevel": "WARN"
- },
- "org.hibernate": {
- "configuredLevel": "INFO",
- "effectiveLevel": "INFO"
- },
- "org.springframework": {
- "configuredLevel": "INFO",
- "effectiveLevel": "INFO"
- }
- }
- }
2、查询日志接口源码
3、设置日志级别接口
post http://127.0.0.1:8081/ebs/actuator/loggers/{name}
url中的name对应日志文件中的模块名称,例如ROOT, com.XX.XX包
4、设置日志的接口代码
5、测试可行性
写一个接口,最基本的代码。如果打印信息符合设置的级别代表设置成功
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF.