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时务必清理上下文

原始标题:Structured Logging in Spring Boot | Baeldung