排查问题是开发人员经常要做的事情,日志是我们常用的排查问题手段,在生产环境我们通常会把日志级别调高,屏蔽我们不关心的日志,那么我们是怎么处理的呢,在spring boot中又有没有更好的处理方案呢
一、低版本处理方式
可能我们在低版本中是这样处理的:
@SuppressWarnings("unckecked")
@GetMapping("/setLogger")
public Result<String> setLogger(String level) {
Result<String> result = new Result<>();
Level logLevel = "1".equals(level) ? Level.ERROR : Level.DEBUG;
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
org.apache.logging.log4j.core.config.Configuration config = ctx.getConfiguration();
Map<String, LoggerConfig> loggerConfigs = config.getLoggers();
loggerConfigs.forEach((name, loggerConfig) -> loggerConfig.setLevel(logLevel));
ctx.updateLoggers(config);
return result.setData("日志级别调整到"+logLevel);
}
二、Spring Boot 处理方式
spring boot 1.5之后我们可以用Spring Boot Actuator
处理:
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、添加配置暴露loggers,默认只暴露health和info
server:
port: 8088
spring:
application:
name: logtest
management:
endpoints:
web:
exposure:
include:
- info
- health
- loggers #默认只暴露 /health 以及 /info 端点
3、写测试demo并
package com.jarvan.test.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Jarvan
*/
@RestController
@Slf4j
public class LogController {
@GetMapping("/testLog")
public String testLog() {
log.debug("这是一个debug日志...");
return "test log";
}
}
4、查看当前应用各包/类的日志级别
5、查看指定包/类日志详情
访问http://localhost:8088/actuator/loggers/com.jarvan.test.controller
6、调用http://localhost:8088/testLog
因为我们日志级别是info所以debug是不会输出的,下面开始改日志级别
7、修改指定包/类日志级别
cmd 运行如下命令或使用postman类似工具
curl "http://localhost:8088/actuator/loggers/com.jarvan.test.controller" -H "Content-Type: application/json" -d "{\"configuredLevel\":\"debug\"}" -X POST
8、查看com.jarvan.test.controller
包级别已经更改为DEBUG
http://localhost:8088/actuator/loggers/com.jarvan.test.controller
9、验证日志级别
再次调用http://localhost:8088/testLog
并查看控制台是否有输出
三、原理简析
Actuator约定把 /actuator/xxx
端点的代码定义在 xxxEndpoint
里面,所以我们找到LoggersEndpoint,它在 org.springframework.boot.actuate.logging
包下面
其中, Endpoint
、WriteOperation
、@Selector
都是Spring Boot 2.0开始提供的新注解。
@Endpoint(id = "loggers")
用来描述Spring Boot Actuator
的端点,这样就会产生一个/actuator/loggers
的路径,它类似于Spring MVC的 @RequestMapping("loggers")
。
@WriteOperation
表示这是一个写操作,它类似于Spring MVC的 @PostMapping
。
Spring Boot Actuator还提供了其他操作,如下表:
Operation | HTTP method |
---|---|
@ReadOperation | GET |
@WriteOperation | POST |
@DeleteOperation | DELETE |
@Selector
用于筛选 @Endpoint
注解返回值的子集,它类似于Spring MVC的 @PathVariable
。
这样,上面的代码就很好理解了—— configureLogLevel
方法里面就一行代码 :this.loggingSystem.setLogLevel(name, configuredLevel);
,发送POST请求后,name就是我们传的包名或者类名,configuredLevel就是我们传的消息体。
然后我们点进这个方法中看具体的实现
setLogLevel一共有四种实现
# 适用于java.util.logging的LoggingSystem
org.springframework.boot.logging.java.JavaLoggingSystem
# 适用于Log4j 2的LoggingSystem
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem
# 适用于logback的LoggingSystem
org.springframework.boot.logging.logback.LogbackLoggingSystem
# 啥都不干的LoggingSystem
org.springframework.boot.logging.LoggingSystem.NoOpLoggingSystem
找个实现点进去
public void setLogLevel(String loggerName, LogLevel logLevel) {
Level level = (Level)LEVELS.convertSystemToNative(logLevel);
LoggerConfig logger = this.getLogger(loggerName);
if (logger == null) {
logger = new LoggerConfig(loggerName, level, true);
this.getLoggerContext().getConfiguration().addLogger(loggerName, logger);
} else {
logger.setLevel(level);
}
this.getLoggerContext().updateLoggers();
}
我们发现Spring Boot本质上还是使用了log底层API的LoggerConfig.setLevel()方法实现日志级别的修改
你可能会好奇,LoggingSystem有这么多实现类,Spring Boot怎么知道什么情况下用什么LoggingSystem呢?可在 org.springframework.boot.logging.LoggingSystem
找到类似如下代码:
由代码不难发现,其实就是构建了一个名为 SYSTEMS
的map,作为各种日志系统的字典;然后在 get
方法中,看应用是否加载了map中的类;如果加载了,就通过反射,初始化响应 LoggingSystem
。例如:Spring Boot发现当前应用加载了 ch.qos.logback.core.Appender
,就去实例化 org.springframework.boot.logging.logback.LogbackLoggingSystem
。
四、可视化界面
Spring Boot Admin
配置请移步 Spring Boot Admin配置可视化页面
本文是使用 curl
手动发送 POST
请求手动修改日志级别的,该方式不适用生产,因为很麻烦,容易出错。生产环境,建议根据Actuator提供的RESTful API定制界面,或使用 Spring Boot Admin
,可视化修改日志级别,如下图所示:
想修改哪个包/类的日志级别,直接点击即可。