Until now the honest answer was: you, in a terminal, moments after the line was printed. For that reader, the classic format is perfect — your eye finds the level by colour and the message by position:
2026-07-13T14:02:11.482+02:00 INFO 8352 --- [nio-8080-exec-1] c.h.taskapp.TaskService : Task 42 created by user 7
But this week your logs get a second reader: software. A log tool that stores millions of lines must answer questions like "give me only ERROR lines from this app, last 24 hours". To do that reliably it has to pull the timestamp, the level, and the logger out of each line. With free-form text, that means guessing where each part starts and ends. Every log format guesses differently, and guesses break.
The fix is simple: stop making machines parse free text logs. Emit each log record as data.
Here is that exact log record as structured JSON — same information, but every part has a named field:
{"@timestamp":"2026-07-13T12:02:11.482Z","log.level":"INFO","log.logger":"net.hackyourfuture.taskapp.TaskService","message":"Task 42 created by user 7","process.thread.name":"http-nio-8080-exec-1"}
A JSON format can be easily read and parsed by a tool. It can now filter on log.level, group by log.logger, and sort by @timestamp — much like querying columns in SQL.
The field names above are not random — they follow the **Elastic Common Schema (ECS)**, a widely used naming convention from the makers of the Elastic/ELK stack, so different tools agree on what fields are called.
<aside> 💭
Structured logging simply means: log records with named fields instead of free text.
</aside>
Recent Spring Boot versions can switch the console to structured output with a single property — no new code:
logging:
structured:
format:
console: ecs
<aside> ⌨️
Hands on: add the property to your project, restart, and inspect at the console. Then trigger the ERROR line from your catch block — notice how even the stack trace becomes a field (error.stack_trace).
</aside>
Structured logging is useful for other software, but it is difficult for humans to read. That tension is real, and professionals resolve it with a simple rule:
So after this experiment, remove the property again. Your console goes back to the human format — and in the next chapter, the copy of each log record that leaves your app will carry its fields (level, logger, app, environment) as data, without making your terminal unreadable.
<aside> 💡
One thing to be clear about: logging.structured.format.console changes your console output only. It has no effect on what gets shipped to a log tool in the next chapter — that is configured separately, in the shipping tool itself. Here it is purely a demo, so you can see what "structured" means before you rely on it.
</aside>
💬 A teammate says: "Why bother? I can just use Ctrl+F on the plain text and search for the word ERROR." What goes wrong with that approach?
ecs, logstash, gelf) and how to add custom fields.The HackYourFuture curriculum is licensed under CC BY-NC-SA 4.0 *https://hackyourfuture.net/*

Built with ❤️ by the HackYourFuture community · Thank you, contributors
Found a mistake or have a suggestion? Let us know in the feedback form.