在 Spring Boot 应用中,默认使用 Logback
来记录日志。可以在 application.yaml
或者是 logback-spring.xml
中配置 Logger 的日志级别。
有以下几个常见的日志级别(从低到高):
TRACE
(跟踪):最低级别的日志,用于输出详细的调试信息,通常用于追踪代码的执行路径。DEBUG
(调试):用于输出调试信息,帮助开发人员调试应用程序。INFO
(信息):用于输出一般性的信息,例如应用程序的启动信息、重要事件等。WARN
(警告):用于输出警告信息,表示潜在的问题或不符合预期的情况,但不会影响应用程序的正常运行。ERROR
(错误):最高级别的日志,用于输出错误信息,表示发生了一个错误或异常情况,可能会影响应用程序的正常运行。
级别越低,包含的信息越详细,级别越高,包含的信息越严重和重要。当设置日志级别时,只有达到或高于该级别的日志才会被记录和输出,而低于该级别的日志将被忽略。如:如果将日志级别设置为 INFO
,则会记录和输出 INFO
、WARN
和 ERROR
级别的日志,而 DEBUG
和 TRACE
级别的日志将被忽略。这有助于控制日志的详细程度和输出量,以适应特定的调试或生产环境要求。
Log4j 建议只使用
ERROR
、WARN
、INFO
、DEBUG
四个级别。
以 ROOT
Logger 的配置为例 {#以-root-logger-的配置为例}
在 application.yaml
中配置 {#在-applicationyaml-中配置}
logging:
level:
ROOT: DEBUG
在 logback-spring.xml
中配置 {#在-logback-springxml-中配置}
首先,需要在 application.yaml
中配置 logback-spring.xml
配置文件的位置:
logging:
config: classpath:logback-spring.xml
然后,在 logback-spring.xml
中配置 ROOT
Logger 的日志级别:
<configuration>
<!-- 继承 Spring 预定义的 Logback 配置 -->
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- 忽略其他的日志配置 ... -->
<!-- ROOT Logger 的日志级别 -->
<root level="DEBUG">
<!-- 输出到控制台 -->
<appender-ref ref="CONSOLE" />
</root>
</configuration>
在运行时修改 Logger 的日志级别 {#在运行时修改-logger-的日志级别}
在使用配置文件的情况下,如果想要修改某个 Logger 的日志级别就要修改配置文件,然后重新启动应用。
好在日志实现库提供了对应的 API 来在运行时动态地修改 Logger 的日志级别,通过这些 API 我们可以很轻松地在应用中管理 Logger。
logback-spring.xml
可以通过配置,定时地检测配置修改,但是这会带来额外的资源消耗。<configuration scan="true" scanPeriod="30 seconds" > <!-- ... 具体配置 --> </configuration>
Sl4j 和 Logback {#sl4j-和-logback}
如上所述,Spring Boot 默认使用 Logback
作为日志实现,但是在 Spring Boot 应用中,我们往往使用 sl4j
的 API 来记录日志。
org.slf4j.Logger
所以,Sl4j 和 Logback 是什么关系?
简而言之,SLF4J (Simple Logging Facade for Java)是一个日志门面,而 Logback 是 SLF4J 的一个实现,用于实际记录日志。开发人员可以使用 SLF4J 接口编写日志代码,并选择 Logback 或其他 SLF4J 实现作为底层日志记录器。这种分离的设计允许在不改变应用代码的情况下更换日志实现。
LoggerController {#loggercontroller}
创建 LoggerController
, 用于管理系统中的 Logger。
package cn.springdoc.demo.web.controller;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
/**
* Logger 管理
* @author springdoc.cn
*/
@RestController
@RequestMapping("/loggers")
public class LoggerController {
// Logger
// 注意,这里使用的是 org.slf4j.Logger 接口
static final org.slf4j.Logger log = LoggerFactory.getLogger(LoggerController.class);
/**
* 查看logger列表
* @return
*/
@GetMapping
public ResponseEntity<List<Map<String, String>>> loggers() {
// 获取到 LoggerContext
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// 获取系统中定义的所有 logger
List<Map<String, String>> loggers = loggerContext.getLoggerList().stream().map(logger -> {
// 映射为 Map,key 是 logger 名称,value 是其日志级别
// logger名称 = logger有效级别
return Collections.singletonMap(logger.getName(), logger.getEffectiveLevel().levelStr);
}).collect(Collectors.toList());
return ResponseEntity.ok(loggers);
}
/**
* 修改日志级别
* @param name
* @param level
* @return
*/
@PostMapping
public ResponseEntity<Object> setLevel(@RequestParam("name") String name,
@RequestParam("level") String level) {
// 获取到 LoggerContext
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// 根据指定的名称获取 logger
Logger logger = loggerContext.exists(name);
if (logger == null) {
return ResponseEntity.badRequest().body(name + " 日志记录器不存在");
}
// 解析 level 参数,第二个参数表示当 level 参数非法时的默认值
Level newLevel = Level.toLevel(level, null);
if (newLevel == null) {
return ResponseEntity.badRequest().body(level + " 不是合法的日志级别");
}
// 重写设置 logger 的 level
logger.setLevel(newLevel);
return ResponseEntity.noContent().build();
}
/**
* 测试日志输出
* @return
*/
@GetMapping("/test")
public ResponseEntity<Void> test (){
log.debug("Hello springdoc.cn");
log.info("Hello springdoc.cn");
log.warn("Hello springdoc.cn");
log.error("Hello springdoc.cn");
return ResponseEntity.noContent().build();
}
}
如上,首先在类成员变量为 Controller 定义了一个 log
Logger,注意这里使用了 sl4j 全路径 org.slf4j.Logger
(避免和 Logback 的 Logger
实现冲突),该 Logger 的名称为类的全路径,即:cn.springdoc.demo.web.controller.LoggerController
。
loggers()
方法是一个 @GetMapping
端点,它通过 Logback
的 LoggerContext
API 获取到系统中定义的所有 Logger 实例,映射为 Map
(Logger 名称为 KEY,Logger 日志级别为 Value)后返回给客户端。
setLevel
方法是一个 @PostMapping
端点,用于动态地修改指定 Logger 的日志级别。它接受 2 个表单参数:
name
:Logger 的名称level
:日志级别,它是一个枚举值:ALL
、TRACE
、DEBUG
、INFO
、WARN
和ERROR
。
setLevel
方法会根据 name
参数检索系统中的 Logger,如果 Logger 不存在则给客户端返回错误信息。然后解析 level
参数为 ch.qos.logback.classic.Level
对象,同样,如果 level
参数非法的话,也会给客户端返回错误信息。最后,修改 Logger 的日志级别。
最后还有一个 test()
方法,用于测试修改是否生效。该方法中,使用 log
输出了 debug
、info
、warn
和 error
级别的 4 条日志,级别由低到高。
测试 {#测试}
首先,在 application.yaml
中配置默认的日志级别为 DEBUG
。
logging:
level:
ROOT: DEBUG
启动应用,首先访问 http://localhost:8080/loggers
获取系统中的所有 Logger:
[
{
"ROOT": "DEBUG"
},
// .... 省略其他无关的 Logger
// LoggerController Logger
{
"cn.springdoc.demo.web.controller.LoggerController": "DEBUG"
},
]
返回的 JSON 数组就是系统中的所有 Logger 定义,KEY 就是 Logger 名称,VALUE 则是其日志级别。实际上,整个系统中定义的 Logger 有数百个,这里为了方便演示,删减了其他所有无关的 Logger。
然后,访问 localhost:8080/loggers/test
测试端点,查看服务端控制台的日志输出:
DEBUG 5344 --- [nio-8080-exec-4] c.s.d.web.controller.LoggerController : Hello springdoc.cn
INFO 5344 --- [nio-8080-exec-4] c.s.d.web.controller.LoggerController : Hello springdoc.cn
WARN 5344 --- [nio-8080-exec-4] c.s.d.web.controller.LoggerController : Hello springdoc.cn
ERROR 5344 --- [nio-8080-exec-4] c.s.d.web.controller.LoggerController : Hello springdoc.cn
如上,由于默认配置的日志级别为 DEBUG
,所以测试方法中的所有日志都会正常输出。
现在,尝试调用 localhost:8080/loggers
接口,修改 cn.springdoc.demo.web.controller.LoggerController
Logger 的日志级别为 INFO
:
curl -d "name=cn.springdoc.demo.web.controller.LoggerController&level=INFO" -X POST http://localhost:8080/loggers
然后,再次请求 localhost:8080/loggers/test
测试端点,并查看服务端控制台的日志输出:
INFO 5344 --- [nio-8080-exec-6] c.s.d.web.controller.LoggerController : Hello springdoc.cn
WARN 5344 --- [nio-8080-exec-6] c.s.d.web.controller.LoggerController : Hello springdoc.cn
ERROR 5344 --- [nio-8080-exec-6] c.s.d.web.controller.LoggerController : Hello springdoc.cn
设置成功,低于 INFO
级别的 DEBUG
日志已经不会被输出到控制台了。
接着,重新修改 cn.springdoc.demo.web.controller.LoggerController
Logger 的日志级别为 DEBUG
:
curl -d "name=cn.springdoc.demo.web.controller.LoggerController&level=DEBUG" -X POST http://localhost:8080/loggers
再次请求测试端点,查看服务端控制台输出:
DEBUG 5344 --- [nio-8080-exec-9] c.s.d.web.controller.LoggerController : Hello springdoc.cn
INFO 5344 --- [nio-8080-exec-9] c.s.d.web.controller.LoggerController : Hello springdoc.cn
WARN 5344 --- [nio-8080-exec-9] c.s.d.web.controller.LoggerController : Hello springdoc.cn
ERROR 5344 --- [nio-8080-exec-9] c.s.d.web.controller.LoggerController : Hello springdoc.cn
一切 OK,Logger 的日志级别修改为 DEBUG
后,测试方法中所有级别的日志记录都正常输出了。