Migrating from Log4j 2 to Logback in Spring Boot: Key Configuration Differences

When migrating from Log4j 2 to Logback in Spring Boot, you must change configuration file names from log4j2.xml to logback.xml, replace ${sys:...} placeholders with Spring Boot's ${...} syntax, and adapt to different default configuration structures while the framework automatically handles the Java Util Logging bridge.

Spring Boot provides first-class support for both Log4j 2 and Logback in the spring-projects/spring-boot repository, but switching between these logging implementations requires understanding how each system discovers configuration files, resolves property placeholders, and integrates with Spring Boot's logging abstractions. This guide examines the core architectural differences to ensure a smooth Log4j to Logback configuration migration.

Configuration File Discovery and Loading

The most immediate difference you'll encounter when migrating is how each logging system discovers its configuration files on the classpath.

Log4j 2 Configuration Factory Approach

Log4j 2 relies on its ConfigurationFactory mechanism to locate configuration files. In Log4J2LoggingSystem.java, Spring Boot bypasses the standard discovery process and explicitly calls load() with a specific location:

// From Log4J2LoggingSystem.java
load(LogFile logFile, String location, List<Configuration> configurations)

This method processes files like log4j2.xml or log4j2-file.xml from the classpath. Spring Boot cannot use the classic "standard locations" approach with Log4j 2 because the ConfigurationFactory API doesn't expose the necessary hooks for Spring Boot's environment integration.

Logback Standard Location Discovery

In contrast, LogbackLoggingSystem.java implements a well-known location discovery mechanism that searches for:

If none of these files are found, Spring Boot falls back to its own default configuration (base.xml). This discovery happens in the initialize() method of LogbackLoggingSystem, which gives Spring Boot fine-grained control over the initialization process.

Default Configuration Structure

When no custom configuration file is present, each system provides radically different default setups.

Logback base.xml and defaults.xml

Logback's default configuration is defined in org/springframework/boot/logging/logback/base.xml. This file:

  1. Imports defaults.xml which defines conversion rules like clr (color) and wEx (whitespace exception)
  2. Sets up both console and file appenders
  3. Defines color-aware patterns using Spring Boot's placeholder resolution

The defaults.xml file provides rich formatting options including the %clr converter for ANSI colors and %wEx for wrapped exceptions, which are automatically available in your logback.xml via the include mechanism.

Log4j 2 Default Configuration

Log4j 2's default configuration lives in org/springframework/boot/logging/log4j2/log4j2.xml. This minimal configuration:

  • Defines only a console appender (no file appender by default)
  • Uses ${sys:...} syntax for property resolution
  • Includes a <Select> element that can switch between StructuredLogLayout and PatternLayout based on the CONSOLE_LOG_STRUCTURED_FORMAT system property

Unlike Logback's rich defaults, Log4j 2's default is intentionally minimal, requiring more explicit configuration for features like file logging or structured output.

Property Placeholders and Syntax Differences

One of the most critical Log4j to Logback configuration migration concerns is the different placeholder syntax used by each system.

Log4j 2 ${sys:...} Syntax

Log4j 2 uses the ${sys:PROPERTY_NAME} syntax to reference system properties. In Log4J2LoggingSystem, Spring Boot injects its own properties (like CONSOLE_LOG_PATTERN) via PropertiesUtil before the configuration is parsed:

// Properties are set via PropertiesUtil in Log4J2LoggingSystem
PropertiesUtil.getProperties().setStringProperty(key, value);

This means your log4j2.xml must use ${sys:LOG_DATEFORMAT_PATTERN} or ${sys:CONSOLE_LOG_PATTERN} to reference Spring Boot's logging properties.

Logback ${...} Syntax

Logback uses Spring Boot's standard ${PROPERTY_NAME} placeholder syntax, resolved by LogbackLoggingSystem through LogbackLoggingSystemProperties. The same property names (LOG_DATEFORMAT_PATTERN, LOG_LEVEL_PATTERN, etc.) are used, but the resolution mechanism is different:

<!-- In logback.xml -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN}}){faint} %clr(${LOG_LEVEL_PATTERN}) %clr(%pid){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD}}"/>

When migrating, you must replace all ${sys:...} references with ${...} and ensure you include the default Spring Boot includes (defaults.xml) to access the conversion rules.

Java Util Logging Bridge Handling

Both systems handle the Java Util Logging (JUL) bridge automatically, but use different implementations.

Logback SLF4JBridgeHandler

For Logback, LogbackLoggingSystem.beforeInitialize() installs the SLF4J bridge:

// From LogbackLoggingSystem.java
loggerContext.getTurboFilterList().add(SUPPRESS_ALL_FILTER);
SLF4JBridgeHandler.install(); // installs JUL → SLF4J bridge

This routes all JUL calls through SLF4J to Logback, using a TurboFilter to suppress logging during the bootstrap phase.

Log4j 2 Log4jBridgeHandler

For Log4j 2, Log4J2LoggingSystem.configureJdkLoggingBridgeHandler() installs the Log4j 2 bridge:

// From Log4J2LoggingSystem.java
if (isJulUsingASingleConsoleHandlerAtMost() && !isLog4jLogManagerInstalled()
    && isLog4jBridgeHandlerAvailable()) {
    removeDefaultRootHandler();
    Log4jBridgeHandler.install(false, null, true); // installs JUL → Log4j2 bridge
}

The migration requires no code changes here—Spring Boot automatically swaps the appropriate bridge based on the active logging system detected on the classpath.

Programmatic Log Level Changes

Both systems support runtime log level changes via LoggingSystem.setLogLevel(), but implement this differently in their respective LoggingSystem implementations.

Logback Level Changes

LogbackLoggingSystem.setLogLevel() works with Logback Level objects and adds a TurboFilter-based suppressor while the system is being bootstrapped:

// Conceptual implementation in LogbackLoggingSystem
void setLogLevel(String loggerName, LogLevel logLevel) {
    ch.qos.logback.classic.Logger logger = getLogger(loggerName);
    logger.setLevel(Level.toLevel(logLevel.name()));
}

Log4j 2 Level Changes

Log4J2LoggingSystem.setLogLevel() works with Log4j 2 Level objects and creates a custom LoggerConfig when needed:

// Conceptual implementation in Log4J2LoggingSystem
void setLogLevel(String loggerName, LogLevel logLevel) {
    LoggerConfig loggerConfig = getLoggerConfig(loggerName);
    if (loggerConfig == null) {
        loggerConfig = new LoggerConfig(loggerName, level, true);
        ctx.getConfiguration().addLogger(loggerName, loggerConfig);
    }
    loggerConfig.setLevel(convertToLog4jLevel(logLevel));
}

Structured Logging Format Support

Both frameworks support structured logging (JSON output), but use different implementations and configuration approaches.

Log4j 2 StructuredLogLayout

Log4j 2 provides its own StructuredLogLayout implementation in org.springframework.boot.logging.log4j2.StructuredLogLayout:

// From StructuredLogLayout.java
@Plugin(name = "StructuredLogLayout", category = Node.CATEGORY)
public class StructuredLogLayout extends AbstractStringLayout {
    // Implements structured formatting for Log4j 2
}

This layout is referenced in the default log4j2.xml using the <Select> element to switch between structured and pattern layouts based on the CONSOLE_LOG_STRUCTURED_FORMAT property.

Logback JsonWriterStructuredLogFormatter

Logback uses JsonWriterStructuredLogFormatter and a set of XML-based appenders:

// From JsonWriterStructuredLogFormatter.java
public class JsonWriterStructuredLogFormatter<T> implements StructuredLogFormatter<T> {
    // Implements JSON formatting for Logback
}

Configuration uses includes like structured-console-appender.xml and structured-file-appender.xml rather than layout plugins.

Summary

  • Configuration file discovery differs significantly: Log4j 2 uses ConfigurationFactory with explicit loading via Log4J2LoggingSystem.load(), while Logback uses standard location discovery (logback.xml, logback-test.xml) through LogbackLoggingSystem.
  • Placeholder syntax must change from Log4j 2's ${sys:...} to Logback's ${...} when referencing Spring Boot properties like CONSOLE_LOG_PATTERN or LOG_DATEFORMAT_PATTERN.
  • Default configurations vary in complexity: Logback provides rich defaults via base.xml and defaults.xml with color converters and file appenders, while Log4j 2 offers a minimal console-only default in log4j2.xml.
  • JUL bridges are handled automatically by Spring Boot, using SLF4JBridgeHandler for Logback and Log4jBridgeHandler for Log4j 2 without requiring manual code changes.
  • Structured logging implementations differ: Log4j 2 uses StructuredLogLayout while Logback uses JsonWriterStructuredLogFormatter with XML-based structured appenders.

Frequently Asked Questions

What file name should I use when switching from Log4j 2 to Logback?

When migrating Log4j to Logback configuration, rename your configuration file from log4j2.xml (or log4j2-file.xml) to logback.xml or logback-spring.xml. Spring Boot's LogbackLoggingSystem automatically searches for these well-known locations in the classpath, whereas Log4J2LoggingSystem requires explicit file paths passed to its internal load() method.

How do I convert Log4j 2 property placeholders to Logback syntax?

Replace Log4j 2's ${sys:PROPERTY_NAME} syntax with Spring Boot's standard ${PROPERTY_NAME} placeholders. In Log4J2LoggingSystem.java, properties are injected via PropertiesUtil using the ${sys:...} prefix, while LogbackLoggingSystem.java resolves placeholders through LogbackLoggingSystemProperties using the standard ${...} syntax. Ensure you include org/springframework/boot/logging/logback/defaults.xml in your Logback configuration to access the standard conversion rules.

Does Spring Boot automatically handle the Java Util Logging bridge when I switch logging systems?

Yes, Spring Boot automatically installs the appropriate JUL bridge based on the active logging system detected on the classpath. For Logback, LogbackLoggingSystem.beforeInitialize() installs SLF4JBridgeHandler alongside a TurboFilter to suppress bootstrap logging. For Log4j 2, Log4J2LoggingSystem.configureJdkLoggingBridgeHandler() installs Log4jBridgeHandler after verifying safe conditions. No manual intervention is required during your Log4j to Logback configuration migration.

What are the key differences in structured logging support between Log4j 2 and Logback?

Log4j 2 implements structured logging through StructuredLogLayout in org/springframework/boot/logging/log4j2/StructuredLogLayout.java, which is referenced in XML configuration using the <Select> element to toggle between structured and pattern layouts. Logback uses JsonWriterStructuredLogFormatter in org/springframework/boot/logging/structured/JsonWriterStructuredLogFormatter.java along with XML-based includes like structured-console-appender.xml and structured-file-appender.xml. When migrating structured logging configurations, you must switch from Log4j 2 layout plugins to Logback's formatter-based XML includes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →