Saltar al contenido
Mis Guidelines
Guías
En esta guía (6 elementos)

Observability — Structured Logging

Observability — Structured Logging

Project-agnostic. Part of the Observability suite. Covers: SLF4J + Logback configuration, OTEL trace-id correlation in logs, JSON structured logging for production, logging rules and PII guards.

Replace com.example.platform / platform-service placeholders with your project’s actual package and service name when applying patterns.

Security tie-in: never log secrets, tokens, PII, or full request bodies. See security.md §10.

Companion files:


8. Structured Logging

SLF4J + Logback Configuration with OTEL Trace Correlation

The project uses SLF4J with Logback. Trace correlation is automatic via Micrometer Tracing’s MDC integration.

Current Log Pattern (application.yml)

logging:
pattern:
console: "%d{HH:mm:ss.SSS} [%thread] [trace-id=%X{traceId}] %-5level %logger{5} - %msg%n"
file: "%d{HH:mm:ss.SSS} [%thread] [trace-id=%X{traceId}] %-5level %logger{5} - %msg%n"

For production environments, use JSON-formatted logs for better parsing by log aggregators (ELK, Loki, NewRelic Logs):

Add the dependency to bootstrap/build.gradle.kts:

dependencies {
// Logback JSON encoder for structured logging
implementation("ch.qos.logback.contrib:logback-json-classic:0.1.5")
implementation("ch.qos.logback.contrib:logback-jackson:0.1.5")
// OR use Logstash encoder (more common):
implementation("net.logstash.logback:logstash-logback-encoder:8.0")
}

Create bootstrap/src/main/resources/logback-spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProfile name="local,dev">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [trace-id=%X{traceId}] [span-id=%X{spanId}] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<springProfile name="staging,production">
<appender name="CONSOLE_JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>spanId</includeMdcKeyName>
<customFields>
{"service":"platform-service"}
</customFields>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE_JSON"/>
</root>
</springProfile>
</configuration>

Logging Examples

Kotlin:

import org.slf4j.LoggerFactory
class CreateUserUseCase(
private val userRepository: UserRepositoryPort,
) : CreateUserPort {
companion object {
private val log = LoggerFactory.getLogger(CreateUserUseCase::class.java)
}
override fun execute(command: CreateUserCommand): User {
log.info("Creating user id={}", command.userId)
val user = userRepository.save(command.toUser())
log.info("User created successfully with id={}", user.id)
return user
}
}

Java:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateUserUseCase implements CreateUserPort {
private static final Logger log = LoggerFactory.getLogger(CreateUserUseCase.class);
private final UserRepositoryPort userRepository;
public CreateUserUseCase(UserRepositoryPort userRepository) {
this.userRepository = userRepository;
}
@Override
public User execute(CreateUserCommand command) {
log.info("Creating user id={}", command.userId());
User user = userRepository.save(command.toUser());
log.info("User created successfully with id={}", user.getId());
return user;
}
}

Logging Rules

  1. Use parameterized messages (log.info("msg {}", value)) — never string concatenation.
  2. Never log sensitive data (passwords, tokens, PII beyond what is strictly necessary).
  3. Include domain context (entity IDs, operation names) for traceability.
  4. Log at the right level:
    • ERROR: Unrecoverable failures that need immediate attention.
    • WARN: Recoverable issues or degraded behavior.
    • INFO: Significant business events (created, updated, deleted).
    • DEBUG: Detailed diagnostic information (cache hits, query parameters).
  5. The trace-id is automatically included in every log line via MDC — do not log it manually.