Required Dependencies
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
Log Severity Hierarchy
Levels from lowest to highest: TRACE < DEBUG < INFO < WARN < ERROR < FATAL.
Only messages at or above the configured threshold are emitted.
Configuration File Naming
Logback recognizes: logback.xml, logback-spring.xml, logback-spring.groovy, logback.groovy.
Spring Boot prefers -spring variants to enable profile-specific options. Place the file under src/main/resources. Override via:
logging:
config: classpath:custom-log-config.xml
Root Logger Element
Mandatory element setting the base severity. Attribute level accepts: TRACE, DEBUG, INFO, WARN, ERROR, ALL, OFF.
<root level="debug">
<appender-ref ref="consoleOut" />
<appender-ref ref="fileOut" />
</root>
Appender Element
Defines output formatting strategy. Attributes: name, class. Common strategies: console, file, rolling file.
Console Output via ConsoleAppender
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logbackDemo</contextName>
<appender name="consoleOut" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - [%method,%line] - %msg%n</pattern>
</layout>
</appender>
<root level="INFO">
<appender-ref ref="consoleOut"/>
</root>
</configuration>
Format tokens:
%d{HH:mm:ss.SSS}– timestamp[%thread]– thread identifier%-5level– left-aligned severity in 5-width column%logger{36}– logger name truncated to 36 chars%method,%line– source method and line number%msg– actual message%n– platform newline
File Output via RollingFileAppender
Supports time/size-based log rotation to avoid unbounded growth.
Logger Element
Targets specific package or class. Attributes: name, optional level, optional additivity (default true).
additivity=true causes duplicate entries if parent logger also appends.
<logger name="com.example.dao" level="DEBUG" />
Example usage:
public class Runner {
private static final Logger auditLog = LoggerFactory.getLogger("com.example.dao");
public static void main(String[] args) {
auditLog.info("data access test");
}
}
Property Element
Defines reusable variables. Referenced via ${varName}.
<property name="logDir" value="/var/logs/app" />
<property name="logFmt" value="%d{HH:mm:ss.SSS} %thread %-5level %logger{36} - %msg%n" />
Pattern Layout Tokens
| Token | Meaning | Example | Output |
|---|---|---|---|
%logger{len} |
Logger name, truncated | %logger{20} |
com.examp... |
%class{len} |
Fully qualified caller class | %class{15} |
com.exam... |
%line |
Source line number | %line |
128 |
%date{pattern} |
Event date/time | %date{yyyy-MM-dd HH:mm} |
2024-03-10 14:23 |
%level |
Severity | %level |
INFO |
%thread |
Thread name | %thread |
http-nio-8080-exec-1 |
%msg |
Message text | %msg |
User login success |
%-widthX |
Left-padded width | %-5level |
INFO␣ |
%X{customKey} |
MDC value | %X{reqId} |
abc123 |
Spring Profiles in Configuration
Wrap <root> in <springProfile> blocks to vary settings per environment.
<springProfile name="dev,test">
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</springProfile>
<springProfile name="prod">
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</springProfile>
Complete Sample Configuration
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<property name="logPath" value="E:/logs/app.log" />
<property name="logFmt" value="%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} %file:%line - %msg%n" />
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${logFmt}</pattern>
</encoder>
</appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logPath}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logPath}.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>${logFmt}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="asyncFile" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>10000</queueSize>
<appender-ref ref="file" />
</appender>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="asyncFile" />
</root>
<logger name="com.sample.controller" />
<logger name="com.sample.controller.DashboardCtrl" level="WARN" additivity="false">
<appender-ref ref="console" />
</logger>
</configuration>
Logger Instantiation and Hierarchy
Create a logger via:
private static final Logger eventLog = LoggerFactory.getLogger(DashboardCtrl.class);
Equivalent to passing the clas literal; logger hierarchy forms automatically (com.sample → com.sample.controller → com.sample.controller.DashboardCtrl). Root logger is available via LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).
Set additivity="false" on child loggers to prevent duplicate emission through ancestor appenders.
Appenders Overview
Appenders route log events to destinations. Core types:
ConsoleAppender– outputs to stdout/stderrFileAppender– writes to a single fileRollingFileAppender– rotates based on policies
Rotation policies:
TimeBasedRollingPolicy– rolls at fixed time intervalsSizeAndTimeBasedRollingPolicy– rolls on size or time thresholds
Time-based example:
<appender name="dailyFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/service.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/service.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>180</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
Size-and-time example:
<appender name="errFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/error.%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
<maxFileSize>50MB</maxFileSize>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
Using Logging in Code
No need to check log level before logging; evaluation is optimized. Classic approach:
private static final Logger opLog = LoggerFactory.getLogger(OrderService.class);
opLog.error("failure in processing");
Lombok annotation alternative:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>
@Slf4j
@RestController
public class ApiCtrl {
public void handle() {
log.info("request received");
}
}