Understanding Spring Boot Integration with Log4j2 and Logback: Logging Mechanisms, Configuration, and Hot Level Updates
This article provides a comprehensive analysis of how Spring Boot integrates Log4j2 (and Logback), detailing the internal logging architecture, configuration options, initialization flow, logger groups, and how to perform hot updates of logger levels via the LoggersEndpoint or custom APIs.
Preface
Logging is a familiar yet often overlooked component in development; we frequently print logs without understanding how they are generated, especially when using the powerful Spring Boot framework.
Understanding how logging frameworks work and how Spring Boot integrates Log4j2 or Logback is valuable for extending logging capabilities and even improving system throughput by adjusting logging strategies. This article uses Spring Boot with Log4j2 as an example to explain the inner workings, and shows that switching to Logback follows a similar pattern.
The article follows a depth‑appropriate guideline to fully explain logging in Spring Boot . The following mind map illustrates the overall structure.
Spring Boot version: 2.7.2 Log4j2 version: 2.17.2
Main Content
1. Simple Working Principle of Log4j2
When using Log4j2 , the most common object you interact with is the Logger . A Logger is a simple structure that delegates the actual log output to an Appender . In Log4j2, the Logger itself is just a shell; the real logic resides in the associated LoggerConfig , which determines which Appender s are used and the logger’s level.
The LoggerConfig and Appender definitions are usually placed in a Log4j2.xml configuration file, which is parsed into a Configuration object.
Each <Appenders> entry adds an Appender to the configuration, and each <Loggers> entry adds a LoggerConfig . The relationship between LoggerContext , Configuration , and Logger can be visualized as follows:
Because of this structure, modifying a logger’s configuration is straightforward via the LoggerContext and its Configuration . For example, hot‑updating a logger’s level is easily achieved.
2. Simple Spring Boot Logging Configuration
Spring Boot allows you to provide a Log4j2.xml file, but it also offers several built‑in properties for quick configuration. Below are common properties and their effects.
1. logging.file.name
logging:
file:
name: test.logThis writes log output to test.log in the project root.
2. logging.file.path
logging:
file:
path: /This writes log output to spring.log in the specified directory.
3. logging.level
logging:
level:
com.pww.App: warnThis sets the level of the logger named com.pww.App to warn .
3. Spring Boot Logging Startup Mechanism
Even without a custom Log4j2.xml , Spring Boot can produce nicely formatted logs because it internally initializes the logging framework via LoggingApplicationListener . This listener reacts to three key events:
ApplicationStartingEvent : Published early, before the environment and application context are available.
ApplicationEnvironmentPreparedEvent : Published after the environment is ready.
ApplicationPreparedEvent : Published after the application context is fully prepared but before the container refresh.
When ApplicationStartingEvent is received, LoggingApplicationListener performs two main actions:
Loads the LoggingSystem implementation class from the system property org.springframework.boot.logging.LoggingSystem .
Calls beforeInitialize() on the loaded LoggingSystem to add a filter that blocks all log output until initialization completes.
1. Handling ApplicationStartingEvent
private void onApplicationStartingEvent(ApplicationStartingEvent event) {
this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
this.loggingSystem.beforeInitialize();
}The LoggingSystem (e.g., Log4J2LoggingSystem for Log4j2) controls logging throughout the Spring Boot lifecycle.
2. Handling ApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
SpringApplication springApplication = event.getSpringApplication();
if (this.loggingSystem == null) {
this.loggingSystem = LoggingSystem.get(springApplication.getClassLoader());
}
initialize(event.getEnvironment(), springApplication.getClassLoader());
}The initialize() method performs three tasks:
Transfers logging‑related properties (e.g., logging.pattern.console ) to system properties.
Initializes the actual logging framework (Log4j2 or Logback).
Sets up logger groups and logger levels based on logging.level configuration.
A table maps Spring Boot logging properties to the corresponding system property names (e.g., logging.pattern.console → CONSOLE_LOG_PATTERN ).
3. Handling ApplicationPreparedEvent
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
}
if (this.logFile != null && !beanFactory.containsBean(LOG_FILE_BEAN_NAME)) {
beanFactory.registerSingleton(LOG_FILE_BEAN_NAME, this.logFile);
}
if (this.loggerGroups != null && !beanFactory.containsBean(LOGGER_GROUPS_BEAN_NAME)) {
beanFactory.registerSingleton(LOGGER_GROUPS_BEAN_NAME, this.loggerGroups);
}
}This registers the LoggingSystem , LogFile , and LoggerGroups as beans, completing the logging initialization.
4. Spring Boot Integration with Log4j2 – Detailed Process
When a logging.config property is absent, Spring Boot searches for conventional Log4j2 configuration files (e.g., log4j2.xml , log4j2-spring.xml ) on the classpath. If none are found, it falls back to its built‑in default configuration files ( log4j2.xml or log4j2-file.xml ) located alongside the LoggingSystem implementation.
If logging.config is provided, Spring Boot directly loads the specified file, bypassing the convention search.
Multiple configuration files can be merged using the logging.log4j2.config.override property, which results in a CompositeConfiguration . logging: config: classpath:Log4j2.xml log4j2: config: override: - classpath:Log4j2-custom1.xml - classpath:Log4j2-custom2.xml During initialization, the selected configuration(s) are loaded, started, and set on the LoggerContext . This process creates Appender s, LoggerConfig s, and updates existing Logger instances to use the new configuration. 5. Hot Updating Logger Levels in Spring Boot Loggers have a level that determines which messages they actually emit. While the level defined in code is static, the logger’s effective level can be changed at runtime via the LoggersEndpoint provided by spring-boot-actuator . Enabling HTTP access to this endpoint requires configuration such as: management: server: address: 127.0.0.1 port: 10999 endpoints: web: base-path: /actuator exposure: include: loggers endpoint: loggers: enabled: true GET /actuator/loggers returns all logger names, their configured levels, and effective levels. POST /actuator/loggers/{name} with a JSON body like {"configuredLevel":"DEBUG"} updates the level instantly without restarting the application. Implementation Details The endpoint’s configureLogLevel method first checks if the name corresponds to a LoggerGroup . If so, it updates every logger in the group; otherwise, it directly calls LoggingSystem.setLogLevel(name, level) . The underlying Log4J2LoggingSystem performs the actual update: @Override public void setLogLevel(String loggerName, LogLevel logLevel) { setLogLevel(loggerName, LEVELS.convertSystemToNative(logLevel)); } private void setLogLevel(String loggerName, Level level) { LoggerConfig logger = getLogger(loggerName); if (level == null) { clearLogLevel(loggerName, logger); } else { setLogLevel(loggerName, logger, level); } getLoggerContext().updateLoggers(); } If a LoggerConfig for the given name does not exist, a temporary LevelSetLoggerConfig (a subclass of LoggerConfig ) is created with additive=true so that logging still propagates to parent configurations. 6. Custom Hot‑Update API without Actuator For lightweight scenarios, you can expose a simple REST controller that injects LoggingSystem and calls setLogLevel directly: @RestController public class HotModificationLevel { private final LoggingSystem loggingSystem; public HotModificationLevel(LoggingSystem loggingSystem) { this.loggingSystem = loggingSystem; } @PostMapping("/logger/level") public void setLoggerLevel(@RequestBody SetLoggerLevelParam param) { loggingSystem.setLogLevel(param.getLoggerName(), param.getLoggerLevel()); } public static class SetLoggerLevelParam { private String loggerName; private LogLevel loggerLevel; // getters and setters omitted } } This approach achieves the same hot‑update capability without pulling in the entire actuator dependency. Conclusion In Log4j2, a Logger is merely a shell; the real behavior is defined by its associated LoggerConfig . Spring Boot’s LoggingApplicationListener initiates logging, while LoggingSystem handles configuration discovery, loading, and initialization. Spring Boot supports both convention‑based and explicit logging.config configuration, and it can merge multiple configurations via logging.log4j2.config.override . Logger groups ( LoggerGroup ) simplify bulk level management, and hot‑updating logger levels is straightforward through LoggingSystem —either via the actuator’s LoggersEndpoint or a custom lightweight controller. Click Follow Public Account "Technical Dry Goods" – Timely Delivery!
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.