线上运行时修改springboot日志级别方案
为了减少日志频繁打印带来的性能影响,线上设置的日志级别相对较高。当线上应用出现问题需要我们排查的时候,可能需要适当降低日志级别(例如DEBUG)来打印更多的日志信息帮助定位问题。
传统的修改日志方式需要1、配置里修改日志级别 2、重启应用 3、问题复现排查问题。这个过程需要重启应用,比较麻烦,效率较低。考虑某一种方式能不重启应用的情况下能够动态修改日志级别。
spring actuator支持对日志级别的运行中动态修改。
actuator 默认暴露的http接口只有info和health,需要把loggers端点配置上才能通过调用http接口使用日志查询和配置功能。
在build.gradle文件中
all*.exclude group: 'org.slf4j', module: 'slf4j-log4j12'
all*.exclude group: 'log4j', module: 'log4j'
compile 'org.springframework.boot:spring-boot-starter-log4j2'
和原来的log4j.properties文件相似的结构。不一样的地方是需要在控制台打印里指定
appender.console.filter.threshold.level日志级别,否则默认是error级别。这样即使指定了rootLogger.level比error级别低(例如info),也无法打印info级别的日志。
级别嵌套关系:rootLogger是第一层,appender.console.filter.threshold.level是第二层。打印的日志级别是这两层同时满足的日志级别。
建议appender.console.filter.threshold.level设置成所能接受的最低级别例如debug, 然后只让rootLogger控制日志级别(info, warn,error…),因为Spring Actuator只能支持到第一层的标签例如rootLogger和logger。
log4j2.properties模板
name = PropertiesConfig
# 定义常量
property.filename = /XX/XX/audit.log
# 控制台打印
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd'T'HH:mm:ss'Z'} [%t] %-5p [%c] - %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = debug
# root引入控制台打印
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
# 审计文件打印
appender.rolling.type = RollingFile
appender.rolling.name = AuditFile
appender.rolling.fileName = ${filename}
appender.rolling.filePattern = ${filename}.%d{yyyy-MM-dd}
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = %d{yyyy-MM-dd'T'HH:mm:ss'Z'} [%t] %-5p [%c] - %m%n
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.max = 20
# 引入com.inspur.commmon包下的审计文件打印
logger.rolling.name = com.inspur.common
logger.rolling.level = info
logger.rolling.additivity = false
logger.rolling.appenderRef.rolling.ref = AuditFile
get http://localhost:8080/loggers
response
查询日志级别接口:
org.springframework.boot.actuate.logging.LoggersEndpoint.java
request:
post http://localhost:8080/loggers/{name}
body:
{
"configuredLevel": "ERROR"
}
说明:
设置日志级别接口,在body中设置日志级别。
url中的name对应日志文件中的第一层的标签名称。name=ROOT对应rootLogger, name=com.inspur.common对应日志文件
设置日志级别接口:
org.springframework.boot.actuate.logging.LoggersEndpoint.java
写一个接口,最基本的代码。如果打印信息符合设置的级别代表设置成功。判断条件可以不加。或者调用自己产品的代码中已经实现的接口来测试。集成log4j2不需要修改原有代码。
现象:调用接口查询和设置日志级别都没有报错。设置新的日志级别后再调用查询接口返回的也是设置后的级别。但是控制台打印的日志级别并没有生效,还是打印启动时设置的日志级别及以上的日志信息。
分析:我们的原有代码中集成的是log4j日志框架,actuator接口已经不支持设置log4j日志级别了。调用设置级别的接口不报错,是因为是设置级别最终落到了默认的JavaLoggingSystem这个日志框架上了。但是代码中@Slf4j绑定的日志框架是log4j。所以没有生效。现在的项目中exclude了spring boot默认支持的logback而选用了老版本的log4j。
actuator loggers接口代码:
org.springframework.boot.actuate.logging.LoggersEndpoint.java
这个loggers()就是对外提供的查询日志的接口。我们看到是通过this.loggingSystem这个对象的操作完成日志的查询和级别的配置。
进入LoggingSystem类。在应用启动时会调用LoggingSystem中的public static LoggingSystem get(ClassLoader classLoader)方法。这个方法会选择当前应用正在使用的日志实现框架。
上面两个图看到,会从SYSTEMS这个Map中筛选出当前应用的日志实现框架。当前支持的框架有三个:logback, log4j2LoggingSystem, JavaLoggingSystem。如果没有匹配到配置的日志框架,会默认选中JavaLoggingSystem。log4j不在支持范围内,所以调用actuator的loggers设置日志级别接口会落在JavaLoggingSystem日志框架上。
下面的logging包中也可以看到LoggingSystem支持的是java,log4j2和loback这三个日志实现框架
附:
level:日志输出级别,共有8个级别,按照从低到高为:All < Trace < Debug < Info < Warn < Error < Fatal < OFF