Skip to main content
Advertisement

2.10 Logging Strategy (SLF4J / Logback)

Spring Boot uses SLF4J as a facade and Logback as the default implementation. In production, configure levels, format, and request tracing (MDC).

Reference: Spring Boot 3.2.x (spring-boot-starter-logging includes Logback)


1. SLF4J and Logback

  • SLF4J: Logging API (facade). Application code uses Logger from SLF4J.
  • Logback: SLF4J implementation. Spring Boot includes it via spring-boot-starter-logging, so no extra dependency is needed.
  • logback-classic: SLF4J binding + Logback core. logback-core: Appenders, encoders, etc.

Code depends only on SLF4J; where and how log output is written is controlled by Logback configuration.


2. Using SLF4J

@Slf4j
@Service
public class OrderService {

public void create(OrderRequest req) {
log.info("Order create request: orderId={}", req.getOrderId());
log.debug("Saved: {}", order);
}
}
  • Use placeholders to avoid string concatenation when the level is disabled.

3. application.yml

logging:
level:
root: INFO
com.example: DEBUG
org.hibernate.SQL: DEBUG

4. MDC for Request Tracing

MDC.put("requestId", requestId);
try {
chain.doFilter(request, response);
} finally {
MDC.clear();
}

Add %X{requestId} in the Logback pattern.


5. Logback Configuration (logback-spring.xml)

Place the config at src/main/resources/logback-spring.xml (or logback.xml). Using logback-spring.xml enables Spring Boot extensions (springProfile, springProperty).

5.1 Structure

  • <appender>: Where log output goes (console, file, socket).
  • <logger>: Level and appenders for a package/class.
  • <root>: Top-level logger; default level and appenders for all logs.

5.2 Console and Pattern

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>

<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %X{requestId} %-5level %logger{36} - %msg%n"/>

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

Common pattern tokens

TokenMeaning
%dTimestamp
%threadThread name
%X{key}MDC value
%-5levelLog level (padded)
%logger36Logger name (max 36 chars)
%msgMessage
%nNewline
%exException stack trace

5.3 Logger Inheritance (Hierarchy & Additivity)

Logback loggers form a name-based hierarchy. Logger names usually match package/class (e.g. com.example.service.OrderService).

Hierarchy

  • root (no name): Top-level parent of all loggers.
  • com → child of root
  • com.example → child of com
  • com.example.service → child of com.example
  • com.example.service.OrderService → child of com.example.service

Level inheritance: If a logger has no level set, it uses the parent’s level up to root.

<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>

<logger name="com.example.service" level="DEBUG"/>
  • Logs from com.example.service.OrderService use DEBUG (from com.example.service).
  • Logs from com.example.controller use INFO (inherited from root).

additivity (Appender inheritance)

Default is additivity="true". If a child logger has an appender, its log events go to that appender and are passed up to ancestors’ appenders, so the same log can appear in root’s CONSOLE as well (duplicate output).

  • additivity="false": Events handled by this logger are not passed to the parent. Use when you want a package to log only to a dedicated file and not to root (e.g. CONSOLE).
<logger name="com.example.service" level="DEBUG" additivity="false">
<appender-ref ref="SERVICE_FILE"/>
</logger>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
  • Logs from com.example.service go only to SERVICE_FILE, not to CONSOLE.
  • Logs from com.example.controller (additivity true by default) go only to root → CONSOLE.

Summary: Level is determined by the nearest ancestor with a level. Appenders: with additivity="true", events go to this logger’s appenders and all ancestors’ appenders. Set additivity="false" to avoid duplicates when using package-specific appenders.

5.4 Logging Configuration Options Reference

Quick reference for common Logback and application.yml options.

Log Levels

LevelMeaningWhen to use
TRACEMost verboseFlow tracing, early debugging (rarely in production)
DEBUGDebug infoDevelopment: SQL, parameters, internal state
INFOGeneral infoRequest/response, business events (default in production)
WARNWarningRecoverable issues, deprecation usage
ERRORErrorExceptions, failures, unrecoverable state

Only events at or above the configured level are logged. level="INFO" logs INFO, WARN, ERROR and drops DEBUG and TRACE.

ConsoleAppender options

OptionDescriptionExample
encoderFormat (pattern, charset)See 5.2
patternConversion pattern%d{yyyy-MM-dd HH:mm:ss} %-5level %msg%n
charsetEncodingUTF-8
targetOutput. System.out (default) / System.errSystem.err

RollingFileAppender options

OptionDescriptionExample
fileCurrent log file path${LOG_PATH}/app.log
encoderFormat (pattern, charset)Same as console
rollingPolicyRotation policy (see below)SizeAndTimeBasedRollingPolicy
appendAppend to existing file (default true)true

SizeAndTimeBasedRollingPolicy options

OptionDescriptionExample
fileNamePatternRolled file name. Use %d{...} (date), %i (index)app-%d{yyyy-MM-dd}.%i.log.gz
maxFileSizeMax size per file before roll100MB, 500MB
maxHistoryNumber of rolled files to keep (days when date-based)30
totalSizeCapTotal size cap; oldest files deleted when exceeded3GB
cleanHistoryOnStartDelete old files on startup (default false)true

TimeBasedRollingPolicy

Date-only rotation. Use %d{yyyy-MM-dd} in fileNamePattern and maxHistory for retention. No maxFileSize.

Encoder (PatternLayoutEncoder) options

OptionDescriptionExample
patternConversion pattern%d %level [%thread] %logger - %msg%n
charsetEncodingUTF-8
immediateFlushFlush on each log (default true). false = buffertrue

Pattern conversion words (additional)

TokenDescription
%d{format}Date. e.g. yyyy-MM-dd HH:mm:ss.SSS, ISO8601
%relativeMilliseconds since start
%level / %-5levelLevel (number = min width, - = left align)
%logger{length}Logger name. %logger{36} = abbreviate FQCN to 36 chars
%class / %method / %lineCaller class/method/line (costly)
%ex / %throwableException stack. %ex{short} = one-line summary
%X{key}MDC value. %X = all MDC

AsyncAppender options

OptionDescriptionDefaultNote
queueSizeQueue capacity256When full, discardingThreshold applies
discardingThresholdWhen queue below this %, drop TRACE/DEBUG/INFO. 0 = drop any200 can drop all
includeCallerDataInclude caller (class/line). Costly when asyncfalseUse only if needed
neverBlockWhen queue full: drop (true) vs block (false)falsetrue = drop
appender-refDelegate appenderSingle ref only

application.yml — logging options

KeyDescriptionExample
logging.level.rootRoot logger levelINFO
logging.level.<package>Package-level levelcom.example: DEBUG
logging.file.nameLog file path (full)logs/app.log
logging.file.pathLog directory (file name = spring.log)logs
logging.pattern.consoleConsole pattern%d %-5level %logger{36} - %msg%n
logging.pattern.fileFile patternSame
logging.pattern.dateformatDefault %d date formatyyyy-MM-dd HH:mm:ss.SSS

When logback-spring.xml exists, its appenders and patterns take precedence over logging.pattern / logging.file. Many setups use yml only for levels and XML for patterns and appenders.


6. RollingFileAppender (File Rotation)

In production, logs are often written to files with size- and time-based rotation.

<property name="LOG_PATH" value="logs"/>
<property name="LOG_FILE" value="application"/>

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE}.log</file>
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_FILE}-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
</appender>
  • SizeAndTimeBasedRollingPolicy: New file when date or size (e.g. 100MB) changes; %i for same-day index.
  • maxHistory: Days to keep (30).
  • totalSizeCap: Total size limit for all rolled files.

7. Profile-Specific Config (springProfile)

Use springProfile to switch appenders/levels per environment.

<springProfile name="dev">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>

<springProfile name="prod">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<logger name="com.example" level="INFO"/>
</springProfile>

8. springProperty — Use application.yml

Read values from application.yml for paths and file names.

<springProperty scope="context" name="LOG_PATH" source="logging.file.path" defaultValue="logs"/>
<springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="app"/>

9. AsyncAppender

Use AsyncAppender to buffer log events and write asynchronously, reducing latency on the request thread.

<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<queueSize>512</queueSize>
<discardingThreshold>0</discardingThreshold>
<appender-ref ref="FILE"/>
</appender>

<root level="INFO">
<appender-ref ref="ASYNC_FILE"/>
</root>
  • queueSize: Queue size; when full, discardingThreshold controls how much to drop (TRACE/DEBUG first when > 0).
  • appender-ref: The actual appender that writes (e.g. FILE).

10. JSON Logs (Production Aggregation)

For ELK, CloudWatch, Datadog, one JSON object per line is easier to parse. Use logstash-logback-encoder for JSON output.

implementation 'net.logstash.logback:logstash-logback-encoder:7.4'
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/app-json.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/app-json-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>100MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>requestId</includeMdcKeyName>
<includeMdcKeyName>userId</includeMdcKeyName>
</encoder>
</appender>
  • LogstashEncoder: Outputs timestamp, level, logger, message, exception, and MDC as JSON.
  • includeMdcKeyName: Include MDC keys as JSON fields for search (e.g. requestId, userId).

11. MDC in Practice

  • Request ID: Set in filter/interceptor; MDC.clear() in finally.
  • User ID: Set after authentication.
  • Child threads (@Async, Executor): MDC is not inherited; use a TaskDecorator to copy parent MDC.

12. Production Notes

  • Do not log passwords, tokens, or card numbers; mask if needed.
  • Limit stack traces; use file rotation and maxHistory / totalSizeCap.
  • JSON format helps with ELK/CloudWatch.
  • With AsyncAppender, tune queueSize and discardingThreshold so logs are not dropped under load.

tip

Use DEBUG for SQL in development; keep root at INFO in production. You can control console, file, JSON, and profiles entirely in logback-spring.xml.

Advertisement