Logback

Logging framework developed by founder of Log4j. Provides native SLF4J integration, automatic configuration reload, conditional processing, and archived log compression. Widely adopted as mature and reliable solution.

Logging LibraryJavaSLF4JSpring BootAppendersConfiguration-Based

GitHub Overview

qos-ch/logback

The reliable, generic, fast and flexible logging framework for Java.

Stars3,128
Watchers145
Forks1,310
Created:August 20, 2009
Language:Java
License:Other

Topics

None

Star History

qos-ch/logback Star History
Data as of: 7/17/2025, 11:31 PM

Library

Logback

Overview

Logback is the standard implementation of SLF4J (Simple Logging Facade for Java) and successor to Log4j, designed as a modern Java logging framework. It provides excellent performance, automatic configuration reload, conditional processing, and rich appenders, and is adopted as the default in Spring Boot. Optimized for modern Java development through automatic configuration and deep Spring integration, it demonstrates excellent performance in microservices architecture as well.

Details

Logback 1.4.14 maintains a solid position as the standard choice in the Spring Boot ecosystem as of 2025. Developed by the author of Log4j, leveraging that experience to achieve higher performance and usability. It provides automatic configuration reload functionality, conditional log processing, MD-Context (Mapped Diagnostic Context) for thread-local information management, and rich appenders (Console, File, Rolling, SMTP, Database, etc.), equipped with all features necessary for enterprise-level Java application development.

Key Features

  • Automatic Configuration Reload: Automatic detection and hot reload of configuration file changes
  • Conditional Processing: Environment-specific configuration through conditional branching in XML files
  • Rich Appenders: Diverse output destinations including file, console, rolling, SMTP, database, etc.
  • MD-Context: Thread-local diagnostic information addition and management functionality
  • High Performance: Execution speed and memory efficiency surpassing Log4j
  • Spring Boot Integration: Deep integration and zero-configuration usage through default adoption

Pros and Cons

Pros

  • Immediate usage start without configuration due to default adoption in Spring Boot
  • High interoperability with other logging libraries as SLF4J standard implementation
  • Efficient debugging work in production environments through automatic configuration reload
  • Support for enterprise-level complex logging requirements through rich appenders
  • Easy environment-specific configuration management through Spring Profiles integration
  • Support for modern observability requirements through structured log output and JSON format compatibility

Cons

  • Understanding of XML configuration required to master advanced features
  • Slightly inferior async logging performance compared to Log4j2
  • Complex configuration files may pose readability and maintainability challenges
  • Limited choice of other facade libraries due to SLF4J dependency
  • Initial configuration overhead occurs in non-Spring Boot environments
  • Features may be overkill for lightweight applications

References

Code Examples

Installation and Basic Setup

<!-- Maven dependency -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.14</version>
</dependency>

<!-- SLF4J API (usually included automatically) -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>
// Gradle dependency
implementation 'ch.qos.logback:logback-classic:1.4.14'
implementation 'org.slf4j:slf4j-api:2.0.9'
// Simplest usage example (no configuration file required)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SimpleExample {
    private static final Logger logger = LoggerFactory.getLogger(SimpleExample.class);
    
    public static void main(String[] args) {
        logger.debug("Debug message");
        logger.info("Info message");
        logger.warn("Warning message");
        logger.error("Error message");
        
        // Parameterized logging
        String user = "john_doe";
        int userId = 12345;
        logger.info("User {} logged in with ID {}", user, userId);
    }
}

Basic Logging Operations (Levels, Formatting)

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class BasicLoggingExample {
    private static final Logger logger = LoggerFactory.getLogger(BasicLoggingExample.class);
    private static final Logger serviceLogger = LoggerFactory.getLogger("service");
    private static final Logger dbLogger = LoggerFactory.getLogger("database");
    
    public static void main(String[] args) {
        // Basic log output
        logger.trace("This is a trace message");
        logger.debug("This is a debug message");
        logger.info("Application started successfully");
        logger.warn("This is a warning message");
        logger.error("This is an error message");
        
        // Parameterized log messages (recommended)
        String operation = "database_query";
        long duration = 150L;
        logger.info("Operation '{}' completed in {} ms", operation, duration);
        
        // Using MDC (Mapped Diagnostic Context)
        MDC.put("userId", "user123");
        MDC.put("sessionId", "sess456");
        logger.info("Processing user request");
        
        // Classification through multiple loggers
        serviceLogger.info("Service layer processing");
        dbLogger.debug("Database connection established");
        
        // MDC clear (important)
        MDC.clear();
        
        // Exception logging
        try {
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            logger.error("Arithmetic error occurred", e);
        }
        
        // Structured log information
        logger.info("User action recorded: action={}, timestamp={}, success={}", 
                   "login", System.currentTimeMillis(), true);
    }
}

Advanced Configuration and Customization (Configuration Files, Appenders)

<!-- logback-spring.xml - Spring Boot recommended configuration file -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Property definitions -->
    <property name="LOG_PATH" value="logs"/>
    <property name="APP_NAME" value="myapp"/>
    
    <!-- Spring profile support -->
    <springProfile name="development">
        <property name="LOG_LEVEL" value="DEBUG"/>
    </springProfile>
    <springProfile name="production">
        <property name="LOG_LEVEL" value="INFO"/>
    </springProfile>
    
    <!-- Console appender -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- Colored console appender (for development) -->
    <appender name="COLORED_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%green(%d{HH:mm:ss.SSS}) %blue([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- File appender -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_PATH}/${APP_NAME}.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- Rolling file appender -->
    <appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${APP_NAME}-rolling.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>300MB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- Error-specific file -->
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/error.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>90</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- Asynchronous appender -->
    <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="ROLLING_FILE"/>
        <queueSize>1000</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <includeCallerData>false</includeCallerData>
    </appender>
    
    <!-- Logger configuration -->
    <logger name="com.mycompany.myapp" level="${LOG_LEVEL}"/>
    <logger name="org.springframework" level="WARN"/>
    <logger name="org.hibernate" level="WARN"/>
    <logger name="database" level="DEBUG" additivity="false">
        <appender-ref ref="ROLLING_FILE"/>
    </logger>
    
    <!-- Root logger -->
    <root level="INFO">
        <springProfile name="development">
            <appender-ref ref="COLORED_CONSOLE"/>
        </springProfile>
        <springProfile name="production">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="ASYNC_FILE"/>
            <appender-ref ref="ERROR_FILE"/>
        </springProfile>
    </root>
</configuration>

Structured Logging and Modern Observability Support

<!-- JSON structured logging configuration -->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.4</version>
</dependency>
<!-- logback-spring.xml with JSON support -->
<configuration>
    <!-- JSON structured log appender -->
    <appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/structured.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/structured.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp/>
                <logLevel/>
                <loggerName/>
                <message/>
                <mdc/>
                <arguments/>
                <stackTrace/>
                <pattern>
                    <pattern>
                        {
                            "service": "my-service",
                            "version": "1.0.0"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
    
    <!-- ELK Stack configuration -->
    <appender name="ELK_JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/elk.json</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/elk.%d{yyyy-MM-dd}.%i.json.gz</fileNamePattern>
            <maxFileSize>50MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeContext>true</includeContext>
            <includeMdc>true</includeMdc>
            <customFields>{"environment":"production","service":"my-app"}</customFields>
        </encoder>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="JSON_FILE"/>
        <appender-ref ref="ELK_JSON"/>
    </root>
</configuration>
// Structured logging usage example
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import net.logstash.logback.argument.StructuredArguments;

public class StructuredLoggingExample {
    private static final Logger logger = LoggerFactory.getLogger(StructuredLoggingExample.class);
    
    public void processUserOrder(String userId, String orderId, double amount) {
        // Setting context information with MDC
        MDC.put("userId", userId);
        MDC.put("orderId", orderId);
        MDC.put("traceId", generateTraceId());
        
        try {
            logger.info("Processing order", 
                StructuredArguments.kv("amount", amount),
                StructuredArguments.kv("currency", "USD"),
                StructuredArguments.kv("operation", "order_processing"));
            
            // Business logic processing
            processPayment(amount);
            
            logger.info("Order processed successfully",
                StructuredArguments.kv("status", "success"),
                StructuredArguments.kv("duration_ms", 250));
                
        } catch (Exception e) {
            logger.error("Order processing failed",
                StructuredArguments.kv("status", "error"),
                StructuredArguments.kv("error_type", e.getClass().getSimpleName()),
                e);
        } finally {
            MDC.clear();
        }
    }
    
    private void processPayment(double amount) {
        // Payment processing simulation
        logger.debug("Payment processing started",
            StructuredArguments.kv("amount", amount));
    }
    
    private String generateTraceId() {
        return "trace_" + System.currentTimeMillis();
    }
}

Error Handling and Performance Optimization

<!-- Performance optimization configuration -->
<configuration scan="true" scanPeriod="30 seconds">
    <!-- Asynchronous logging configuration -->
    <appender name="ASYNC_CONSOLE" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="CONSOLE"/>
        <queueSize>2000</queueSize>
        <discardingThreshold>100</discardingThreshold>
        <includeCallerData>false</includeCallerData>
        <neverBlock>true</neverBlock>
    </appender>
    
    <!-- Conditional appender -->
    <if condition='property("spring.profiles.active").contains("production")'>
        <then>
            <appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
                <smtpHost>smtp.company.com</smtpHost>
                <to>[email protected]</to>
                <from>[email protected]</from>
                <subject>Critical Error in ${APP_NAME}</subject>
                <layout class="ch.qos.logback.classic.PatternLayout">
                    <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
                </layout>
                <filter class="ch.qos.logback.classic.filter.LevelFilter">
                    <level>ERROR</level>
                    <onMatch>ACCEPT</onMatch>
                    <onMismatch>DENY</onMismatch>
                </filter>
                <cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker">
                    <bufferSize>10</bufferSize>
                </cyclicBufferTracker>
            </appender>
        </then>
    </if>
    
    <!-- Database appender -->
    <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
        <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
            <driverClass>org.postgresql.Driver</driverClass>
            <url>jdbc:postgresql://localhost:5432/logdb</url>
            <user>loguser</user>
            <password>logpass</password>
        </connectionSource>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="ASYNC_CONSOLE"/>
        <if condition='property("spring.profiles.active").contains("production")'>
            <then>
                <appender-ref ref="SMTP"/>
                <appender-ref ref="DB"/>
            </then>
        </if>
    </root>
</configuration>
// Performance monitoring and error handling
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Service;

@Service
public class PerformanceOptimizedService {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceOptimizedService.class);
    private static final Logger performanceLogger = LoggerFactory.getLogger("performance");
    
    public void performCriticalOperation(String operationId) {
        long startTime = System.currentTimeMillis();
        
        // Setting tracing information
        MDC.put("operationId", operationId);
        MDC.put("startTime", String.valueOf(startTime));
        
        try {
            logger.info("Critical operation started");
            
            // Critical processing simulation
            simulateProcessing();
            
            long duration = System.currentTimeMillis() - startTime;
            
            // Performance logging
            performanceLogger.info("Operation completed successfully in {} ms", duration);
            
            // Alert threshold check
            if (duration > 1000) {
                logger.warn("Operation took longer than expected: {} ms", duration);
            }
            
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            
            // Detailed logging on error
            logger.error("Critical operation failed after {} ms", duration, e);
            
            // Immediate alert for critical errors
            if (isCriticalError(e)) {
                logger.error("CRITICAL ERROR: Immediate attention required", e);
            }
            
            throw e;
            
        } finally {
            MDC.clear();
        }
    }
    
    private void simulateProcessing() throws Exception {
        // Processing time simulation
        Thread.sleep(100 + (int)(Math.random() * 200));
        
        // Error simulation
        if (Math.random() < 0.1) {
            throw new RuntimeException("Simulated processing error");
        }
    }
    
    private boolean isCriticalError(Exception e) {
        return e instanceof NullPointerException || 
               e instanceof OutOfMemoryError ||
               e.getMessage().contains("database");
    }
}

Framework Integration and Practical Examples

// Spring Boot integration example
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SpringBootApplication
public class LogbackSpringBootApplication {
    private static final Logger logger = LoggerFactory.getLogger(LogbackSpringBootApplication.class);
    
    public static void main(String[] args) {
        logger.info("Starting Spring Boot application with Logback");
        SpringApplication.run(LogbackSpringBootApplication.class, args);
        logger.info("Application started successfully");
    }
}
// Spring MVC Controller with Logback
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

@RestController
@RequestMapping("/api/users")
public class UserController {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    
    @GetMapping("/{id}")
    public User getUser(@PathVariable String id, HttpServletRequest request) {
        // Setting request-specific information in MDC
        MDC.put("userId", id);
        MDC.put("requestId", request.getHeader("X-Request-ID"));
        MDC.put("userAgent", request.getHeader("User-Agent"));
        
        try {
            logger.info("Retrieving user information");
            
            User user = userService.findById(id);
            
            if (user != null) {
                logger.info("User found successfully");
            } else {
                logger.warn("User not found");
            }
            
            return user;
            
        } catch (Exception e) {
            logger.error("Error retrieving user information", e);
            throw e;
        } finally {
            MDC.clear();
        }
    }
}
# application.properties Logback configuration
# Log level configuration
logging.level.root=INFO
logging.level.com.mycompany=DEBUG
logging.level.org.springframework=WARN

# File output configuration
logging.file.name=logs/application.log
logging.file.max-size=10MB
logging.file.max-history=30

# Pattern configuration
logging.pattern.console=%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n

# Spring Boot Actuator integration
management.endpoints.web.exposure.include=loggers
management.endpoint.loggers.enabled=true
// Logback configuration in test environment
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.slf4j.LoggerFactory;

@SpringBootTest
@ActiveProfiles("test")
public class LogbackTestExample {
    
    @Test
    public void testLoggingOutput() {
        // Create ListAppender for testing
        Logger logger = (Logger) LoggerFactory.getLogger("com.mycompany.test");
        ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
        listAppender.start();
        logger.addAppender(listAppender);
        
        // Log output
        logger.info("Test log message");
        logger.error("Test error message");
        
        // Verify log output
        List<ILoggingEvent> logsList = listAppender.list;
        assertEquals(2, logsList.size());
        assertEquals("Test log message", logsList.get(0).getMessage());
        assertEquals(Level.INFO, logsList.get(0).getLevel());
        assertEquals("Test error message", logsList.get(1).getMessage());
        assertEquals(Level.ERROR, logsList.get(1).getLevel());
    }
}