Simple Logging - VittorioDeMarzi/hero-beans GitHub Wiki
This page provides a guide for setting up and using logging in our Kotlin + Spring Boot
services deployed on AWS
.
- Debugging: understand what happened and why.
- Operations: investigate incidents in production.
- Analytics: measure flows and performance (with structured logs).
-
TRACE โ ultraโdetailed steps (usually off).
-
DEBUG โ developer details (enable locally / during incidents).
-
INFO โ key business events (default in prod). Examples: order created, payment sent, user authenticated.
-
WARN โ something unexpected, but system continues. Example: fallback used, slow external call.
-
ERROR โ failures that require attention; always log with exception.
Do not log: passwords, tokens, secrets.
TRACE < DEBUG < INFO < WARN < ERROR
๐ The higher the level, the fewer messages will appear in the logs.
-
root = INFO โ shows all INFO, WARN, ERROR from Spring, Hibernate, libraries.
-
techcourse.herobeans = INFO. ๐ Result: verbose logs, good for debugging, we can see requests and responses.
-
root = WARN โ external libraries log only WARN and ERROR.
-
techcourse.herobeans = INFO โ our services still log business INFO (e.g., โCheckout startedโ, โOrder createdโ).
-
org.hibernate = ERROR โ Hibernate logs only errors, no SQL noise. ๐ Result: clean logs โ only your info messages plus important warnings/errors.
-
root = WARN
-
techcourse.herobeans = INFO ๐ Result: tests are not spammed by framework logs, but INFO is still visible โ useful for debugging tests.
Spring Boot already includes SLF4J + Logback for logging. We additionally use Kotlin Logging for more concise and Kotlin-friendly syntax.
dependencies {
implementation("io.github.microutils:kotlin-logging-jvm:3.0.5")
}
import mu.KotlinLogging
private val log = KotlinLogging.logger {}
class OrderService {
fun placeOrder(id: Long) {
log.info { "Placing order id=$id" }
try { /* ... */ }
catch (ex: Exception) {log.error(ex) { "Failed order id=$id" }}
}
}
- Centralize all error logging and error responses.
- Log one error per request (avoid duplicates), with correlation id and request path.
Adds or generates an X-Correlation-Id
for each request.
It stores the ID in MDC so it appears in all logs, and also returns it in the response header, making it easy to trace a single request across logs.
Controllers & Services
-
Added INFO logs for key business actions (e.g., product created, order placed).
-
Rule: one log entry per main action โ helps track normal flow.
-
Example:
log.info { "User ${user.id} placed order ${order.id}" }
GlobalExceptionHandler
-
Logs all handled exceptions at ERROR level (with stacktrace).
-
Includes request path + correlationId for traceability.
-
Example log:
2025-08-20 18:12:41.630 ERROR [0d74ac79-ffae-4b7a-bea0-1a65fb6f5f3b] t.h.exception.GlobalExceptionHandler - Exception at POST /api/address: Max 5 addresses allowed
We configured two rolling log files via logback-spring.xml:
-
application.log
โ contains INFO, WARN (normal application flow). -
general.errors.log
โ contains only ERROR (critical issues).
This helps operations teams quickly find errors without being distracted by normal logs.
This file defines how logging works in our service. It controls:
-
where logs are stored
-
what log levels go into which file
-
how logs are rotated (archived)
<property name="LOG_DIR" value="logs" />
By default logs are written into a logs folder (relative to the project). On server (e.g., AWS EC2) we set an absolute path, like:
<property name="LOG_DIR" value="/home/ubuntu/app/logs" />
<property name="PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%X{correlationId}] %logger{36} - %msg%n"/>
-
Date/time โ 2025-08-20 14:12:45.123
-
Level โ INFO, ERROR, etc.
-
CorrelationId โ unique per request (from CorrelationIdFilter).
-
Logger name โ class name (shortened to 36 chars).
-
Message โ actual log text.
<appender name="APP" ...>
-
Collects INFO and WARN logs (normal application flow).
-
Explicitly excludes ERROR, so errors wonโt appear twice.
-
Rolling policy:
-
Rotate daily and when file reaches 10MB.
-
Keep 14 days of history.
-
๐ Used for business events and operational monitoring.
<appender name="ERR" ...>
-
Collects only ERROR logs.
-
Rolling policy:
-
Rotate daily and when file reaches 10MB.
-
Keep 30 days of history.
-
๐ Used for production troubleshooting (fast access to all failures without noise).
<logger name="techcourse.herobeans" level="INFO" additivity="false">
<appender-ref ref="APP"/>
<appender-ref ref="ERR"/>
</logger>
-
Our code (techcourse.herobeans package)
-
Default level =
INFO
. -
Logs go to both application.log and general.errors.log.
-
additivity="false"
prevents duplication in parent loggers.
-
<root level="WARN">
<appender-ref ref="APP"/>
<appender-ref ref="ERR"/>
</root>
-
All other libraries/frameworks (Spring, Hibernate, etc.).
-
Default level =
WARN
. -
Means: we donโt log their
INFO
spam, but still see warnings & errors.\
-
-
application.log โ INFO + WARN, main system events, short retention (14 days).
-
general.errors.log โ ERROR only, long retention (30 days).
-
Logs are rotated automatically โ no risk of filling disk.