2. 概述
日志是任何软件应用的核心功能。它通过记录错误、警告和其他事件,帮助追踪应用运行时的行为。
默认情况下,Spring Boot应用生成的是非结构化、人类可读的日志。虽然这些日志对开发者有用,但日志聚合工具难以解析和分析。结构化日志解决了这个限制。
本教程将学习如何利用Spring Boot 3.4.0引入的特性实现结构化日志。
3. Maven依赖
首先,在pom.xml
中添加spring-boot-starter
来创建Spring Boot项目:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
这个依赖为典型Spring Boot应用提供了自动配置和日志支持。
4. Spring Boot默认日志
以下是默认的Spring Boot日志示例:
INFO 22059 --- [ main] c.b.s.StructuredLoggingApp : No active profile set, falling back to 1 default profile: "default"
INFO 22059 --- [ main] c.b.s.StructuredLoggingApp : Started StructuredLoggingApp in 2.349 seconds (process running for 3.259)
虽然这些日志信息丰富,但Elasticsearch等工具难以直接处理,也无法用于指标分析。JSON等结构化日志格式通过标准化内容解决了这个问题。
5. 配置
从Spring Boot 3.4.0开始,结构化日志成为内置功能,支持Elastic Common Schema (ECS)、Graylog Extended Log Format (GELF)和Logstash JSON等格式。
可直接在application.properties
中配置结构化日志。
5.1. Elastic Common Schema
ECS是一种标准化的JSON日志格式,可无缝集成Elasticsearch和Kibana。在application.properties
中添加:
logging.structured.format.console=ecs
输出示例:
{
"@timestamp": "2024-12-19T01:17:47.195098997Z",
"log.level": "INFO",
"process.pid": 16623,
"process.thread.name": "main",
"log.logger": "com.baeldung.springstructuredlogging.StructuredLoggingApp",
"message": "Started StructuredLoggingApp in 3.15 seconds (process running for 4.526)",
"ecs.version": "8.11"
}
输出包含可被Elasticsearch和Kibana轻松解析的键值对。
可通过添加服务名、环境等字段增强ECS日志,提升可观测性:
logging.structured.ecs.service.name=MyService
logging.structured.ecs.service.version=1
logging.structured.ecs.service.environment=Production
logging.structured.ecs.service.node-name=Primary
增强后输出:
{
"@timestamp": "2024-12-19T01:25:15.123108416Z",
"log.level": "INFO",
"process.pid": 18763,
"process.thread.name": "main",
"service.name": "BaeldungService",
"service.version": "1",
"service.environment": "Production",
"service.node.name": "Primary",
"log.logger": "com.baeldung.springstructuredlogging.StructuredLoggingApp",
"message": "Started StructuredLoggingApp in 3.378 seconds (process running for 4.376)",
"ecs.version": "8.11"
}
5.2. Graylog扩展日志格式
GELF是另一种支持的JSON结构化日志格式。在application.properties
中启用:
logging.structured.format.console=gelf
输出示例(属性名与ECS不同):
{
"version": "1.1",
"short_message": "Started StructuredLoggingApp in 2.77 seconds (process running for 3.89)",
"timestamp": 1734572549.172,
"level": 6,
"_level_name": "INFO",
"_process_pid": 23929,
"_process_thread_name": "main",
"_log_logger": "com.baeldung.springstructuredlogging.StructuredLoggingApp"
}
可通过添加主机和服务版本增强输出:
logging.structured.gelf.host=MyService
logging.structured.gelf.service.version=1
5.3. Logstash格式
Logstash格式也是开箱即用。在application.properties
中指定:
logging.structured.format.console=logstash
输出示例:
{
"@timestamp": "2024-12-19T02:49:33.017851728+01:00",
"@version": "1",
"message": "Started StructuredLoggingApp in 2.749 seconds (process running for 3.605)",
"logger_name": "com.baeldung.springstructuredlogging.StructuredLoggingApp",
"thread_name": "main",
"level": "INFO",
"level_value": 20000
}
此格式可被支持Logstash的日志聚合工具轻松分析。
5.4. 附加信息
可使用Mapped Diagnostic Context (MDC)类向结构化日志添加更多信息。例如添加userId
以便按用户过滤日志:
private static final Logger LOGGER = LoggerFactory.getLogger(CustomLog.class);
public void additionalDetailsWithMdc() {
MDC.put("userId", "1");
MDC.put("userName", "Baeldung");
LOGGER.info("Hello structured logging!");
MDC.remove("userId");
MDC.remove("userName");
}
⚠️ 注意:后续必须清理MDC上下文,避免内存泄漏。
包含用户详情的日志输出:
{
"@timestamp": "2024-12-19T07:52:30.556819106+01:00",
"@version": "1",
"message": "Hello structured logging!",
"logger_name": "com.baeldung.springstructuredlogging.CustomLog",
"thread_name": "main",
"level": "INFO",
"level_value": 20000,
"userId": "1",
"userName": "Baeldung"
}
也可使用流式日志API实现类似功能:
public void additionalDetailsUsingFluentApi() {
LOGGER.atInfo()
.setMessage("Hello Structure logging!")
.addKeyValue("userId", "1")
.addKeyValue("userName", "Baeldung")
.log();
}
✅ 此方式更简洁,自动处理上下文清理,减少出错可能。
5.5. 自定义日志格式
可定义自定义结构化日志格式并在application.properties
中使用。当内置格式不满足需求时特别有用。
首先实现StructuredLogFormatter
接口并重写format()
方法:
class MyStructuredLoggingFormatter implements StructuredLogFormatter<ILoggingEvent> {
@Override
public String format(ILoggingEvent event) {
return "time=" + event.getTimeStamp() + " level=" + event.getLevel() + " message=" + event.getMessage() + "\n";
}
}
此自定义格式为文本格式(非标准JSON),提供灵活性(可使用JSON/XML等任意格式)。
在application.properties
中配置:
logging.structured.format.console=com.baeldung.springstructuredlogging.MyStructuredLoggingFormatter
输出示例:
time=1734598194538 level=INFO message=Hello structured logging!
更复杂的自定义JSON格式示例:
private final JsonWriter<ILoggingEvent> writer = JsonWriter.<ILoggingEvent>of((members) -> {
members.add("time", ILoggingEvent::getInstant);
members.add("level", ILoggingEvent::getLevel);
members.add("thread", ILoggingEvent::getThreadName);
members.add("message", ILoggingEvent::getFormattedMessage);
members.add("application").usingMembers((application) -> {
application.add("name", "StructuredLoggingDemo");
application.add("version", "1.0.0-SNAPSHOT");
});
members.add("node").usingMembers((node) -> {
node.add("hostname", "node-1");
node.add("ip", "10.0.0.7");
});
}).withNewLineAtEnd();
集成到format()
方法:
@Override
public String format(ILoggingEvent event) {
return this.writer.writeToString(event);
}
输出JSON结构:
{
"time": "2024-12-19T08:55:13.284101533Z",
"level": "INFO",
"thread": "main",
"message": "No active profile set, falling back to 1 default profile: \"default\"",
"application": {
"name": "StructuredLoggingDemo",
"version": "1.0.0-SNAPSHOT"
},
"node": {
"hostname": "node-1",
"ip": "10.0.0.7"
}
}
5.6. 日志输出到文件
前述示例直接输出到控制台。可通过修改配置保持控制台人类可读格式,同时将结构化日志写入文件:
logging.structured.format.file=ecs
logging.file.name=log.json
此配置会在项目根目录创建log.json
文件,包含结构化日志。
6. 总结
本文介绍了如何通过application.properties
配置应用实现结构化日志,展示了多种支持的结构化日志格式示例,并讲解了如何编写自定义格式。
关键点总结:
- ✅ Spring Boot 3.4.0+内置结构化日志支持
- ✅ 支持ECS/GELF/Logstash等主流格式
- ✅ 通过MDC或流式API添加动态字段
- ✅ 支持自定义格式和文件输出
- ⚠️ 使用MDC时务必清理上下文