Structured Logging in Spring Boot 3.4: JSON, ECS, and Custom Formats
Spring Boot 3.4 adds built‑in support for structured logging with ready‑made JSON formats such as Elastic Common Schema, lets you configure console or file outputs, enrich logs via MDC or the fluent API, and enables custom formatters—including a JsonWriter‑based example—for fully tailored, machine‑readable log entries.
Log recording is a core part of application troubleshooting and system observability. Structured logging outputs logs in a predefined, machine‑readable format (commonly JSON), enabling powerful search, filtering, and analysis.
Spring Boot 3.4 provides out‑of‑the‑box support for structured logging. It includes built‑in formats such as Elastic Common Schema (ECS) and Logstash, and can be extended for custom needs.
Enable Structured Logging on the Console
Add the following property to application.properties to output console logs in ECS format:
logging.structured.format.console=ecsWhen the application starts, logs appear as JSON, e.g.:
{"@timestamp":"2024-07-30T08:41:10.561295200Z","log.level":"INFO","process.pid":67455,"process.thread.name":"main","service.name":"structured-logging-demo","log.logger":"com.example.structured_logging_demo.StructuredLoggingDemoApplication","message":"Started StructuredLoggingDemoApplication in 0.329 seconds (process running for 0.486)","ecs.version":"8.11"}Write Structured Logs to a File
Configure file output by adding to application.properties :
logging.structured.format.file=ecs
logging.file.name=log.jsonThe console shows human‑readable logs, while log.json contains the structured JSON entries.
Adding Extra Fields
Use MDC to attach additional data (e.g., userId) to each log event:
@Component
class MyLogger implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLogger.class);
@Override
public void run(String... args) {
MDC.put("userId", "1");
LOGGER.info("Hello structured logging!");
MDC.remove("userId");
}
}The resulting JSON includes the extra field:
{..., "message":"Hello structured logging!", "userId":"1", ...}Alternatively, use the fluent API without MDC:
@Component
class MyLogger implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(MyLogger.class);
@Override
public void run(String... args) {
LOGGER.atInfo().setMessage("Hello structured logging!")
.addKeyValue("userId", "1")
.log();
}
}Custom Log Formatter
Create a class implementing StructuredLogFormatter<ILoggingEvent> and register it in application.properties :
class MyStructuredLoggingFormatter implements StructuredLogFormatter<ILoggingEvent> {
@Override
public String format(ILoggingEvent event) {
return "time=" + event.getTimeStamp() + " level=" + event.getLevel() + " message=" + event.getMessage() + "\n";
}
} logging.structured.format.console=com.example.structured_logging_demo.MyStructuredLoggingFormatterRunning the app now prints logs like:
time=1722330118045 level=INFO message=Hello structured logging!For JSON output, Spring Boot 3.4 introduces JsonWriter :
class MyStructuredLoggingFormatter implements StructuredLogFormatter<ILoggingEvent> {
private final JsonWriter<ILoggingEvent> writer = JsonWriter.<ILoggingEvent>of(members -> {
members.add("time", e -> e.getInstant());
members.add("level", e -> e.getLevel());
members.add("thread", e -> e.getThreadName());
members.add("message", e -> e.getFormattedMessage());
members.add("application").usingMembers(app -> {
app.add("name", "StructuredLoggingDemo");
app.add("version", "1.0.0-SNAPSHOT");
});
members.add("node").usingMembers(node -> {
node.add("hostname", "node-1");
node.add("ip", "10.0.0.7");
});
}).withNewLineAtEnd();
@Override
public String format(ILoggingEvent event) {
return writer.writeToString(event);
}
} logging.structured.format.console=com.example.structured_logging_demo.MyStructuredLoggingFormatterThe generated log looks like:
{"time":"2024-07-30T09:14:49.377308361Z","level":"INFO","thread":"main","message":"Hello structured logging!","application":{"name":"StructuredLoggingDemo","version":"1.0.0-SNAPSHOT"},"node":{"hostname":"node-1","ip":"10.0.0.7"}}Note: Spring Boot 3.0/3.1 lifecycle has ended; upgrade to the latest version.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.