How to Programmatically Change SLF4J Log Levels at Runtime in Spring Boot

You can change SLF4J log levels at runtime in Spring Boot by obtaining the active LoggingSystem instance and calling setLogLevel(String loggerName, LogLevel level) to update specific loggers or the root logger dynamically.

Spring Boot provides a unified abstraction over popular logging frameworks like Logback and Log4J‑2, allowing you to programmatically adjust log levels without restarting your application. This capability is essential for troubleshooting production issues or enabling debug verbosity on demand.

Understanding the LoggingSystem Abstraction

Spring Boot shields your code from the underlying logging implementation through the org.springframework.boot.logging.LoggingSystem interface. This abstraction lives in LoggingSystem.java and declares the core operation setLogLevel(String loggerName, LogLevel level), which concrete implementations override to manipulate the actual logging backend.

When your application starts, Spring Boot uses LoggingSystem.get(ClassLoader) to detect and instantiate the correct implementation. This method checks the LoggingSystem.SYSTEM_PROPERTY or delegates to LoggingSystemFactory implementations that scan the classpath. For example, LogbackLoggingSystem.Factory (found in LogbackLoggingSystem.java lines 86‑92) and Log4J2LoggingSystem.Factory (in Log4J2LoggingSystem.java lines 22‑30) compete to provide the appropriate system based on available libraries.

How setLogLevel Works Under the Hood

The setLogLevel method delegates to framework‑specific APIs that modify the logger configuration immediately. Both implementations ultimately affect the SLF4J ILoggerFactory, ensuring that any code using org.slf4j.Logger sees the updated level without requiring code changes.

Logback Implementation

In LogbackLoggingSystem.java (lines 400‑405), the setLogLevel method retrieves the Logback Logger from LoggerContext and calls setLevel(ch.qos.logback.classic.Level):

// From LogbackLoggingSystem.java
@Override
public void setLogLevel(String loggerName, LogLevel level) {
    ch.qos.logback.classic.Logger logger = getLogger(loggerName);
    logger.setLevel(translateLogLevel(level)); // Maps LogLevel to ch.qos.logback.classic.Level
}

Log4J2 Implementation

In Log4J2LoggingSystem.java (lines 52‑66), the implementation updates the LoggerConfig within the LoggerContext:

// From Log4J2LoggingSystem.java
@Override
public void setLogLevel(String loggerName, LogLevel level) {
    Level translatedLevel = translateLogLevel(level);
    LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    Configuration config = ctx.getConfiguration();
    LoggerConfig loggerConfig = config.getLoggerConfig(loggerName);
    loggerConfig.setLevel(translatedLevel);
    ctx.updateLoggers();
}

Retrieving the Active LoggingSystem

You can obtain the LoggingSystem instance either statically or through dependency injection, depending on whether you are inside a Spring-managed component.

Static Access

Use LoggingSystem.get(ClassLoader) when you need access outside of the Spring context or in static utility methods:

import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LogLevel;

public class RuntimeLogConfigurator {
    
    public static void setDebugForPackage(String packageName) {
        LoggingSystem system = LoggingSystem.get(ClassLoader.getSystemClassLoader());
        system.setLogLevel(packageName, LogLevel.DEBUG);
    }
}

Spring Bean Injection

Within a Spring Boot application, LoggingSystem is registered as a singleton bean. Inject it into your components for cleaner code:

import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LogLevel;
import org.springframework.stereotype.Component;

@Component
public class DynamicLogManager {
    
    private final LoggingSystem loggingSystem;
    
    public DynamicLogManager(LoggingSystem loggingSystem) {
        this.loggingSystem = loggingSystem;
    }
    
    public void enableVerboseLogging(String loggerName) {
        loggingSystem.setLogLevel(loggerName, LogLevel.TRACE);
    }
    
    public void resetRootToInfo() {
        loggingSystem.setLogLevel(null, LogLevel.INFO);
    }
}

Practical Code Examples

Event-Driven Log Level Changes

You can combine the LoggingSystem with Spring's event infrastructure to react to external configuration changes. This example listens for a custom event to update levels dynamically:

import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.LogLevel;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class LogLevelChangeListener implements ApplicationListener<LogLevelChangeEvent> {
    
    private final LoggingSystem loggingSystem;
    
    public LogLevelChangeListener(LoggingSystem loggingSystem) {
        this.loggingSystem = loggingSystem;
    }
    
    @Override
    public void onApplicationEvent(LogLevelChangeEvent event) {
        loggingSystem.setLogLevel(event.getLoggerName(), event.getLevel());
    }
}

The custom event class:

import org.springframework.boot.logging.LogLevel;
import org.springframework.context.ApplicationEvent;

public class LogLevelChangeEvent extends ApplicationEvent {
    private final String loggerName;
    private final LogLevel level;
    
    public LogLevelChangeEvent(Object source, String loggerName, LogLevel level) {
        super(source);
        this.loggerName = loggerName;
        this.level = level;
    }
    
    public String getLoggerName() { return loggerName; }
    public LogLevel getLevel() { return level; }
}

Summary

  • Spring Boot's LoggingSystem provides a framework-agnostic API to programmatically change SLF4J log levels at runtime without restarting the application.
  • Concrete implementations in LogbackLoggingSystem.java (lines 400‑405) and Log4J2LoggingSystem.java (lines 52‑66) translate LogLevel enums to framework-specific level objects.
  • Access patterns include static retrieval via LoggingSystem.get(ClassLoader) or dependency injection as a Spring bean.
  • Pass null as the logger name to setLogLevel to adjust the root logger level.

Frequently Asked Questions

How do I reset a logger to its default level using LoggingSystem?

The LoggingSystem API does not provide a direct "reset to default" method. To restore the initial configuration, you must know the original level and call setLogLevel again with that value. Alternatively, you can reinitialize the logging system entirely, though this is rarely recommended in production as it may cause temporary logging disruption.

Can I use this approach with Java Util Logging (JUL) instead of Logback or Log4J2?

Yes, Spring Boot includes a JavaLoggingSystem implementation that delegates to java.util.logging. When java.util.logging is detected as the primary logging framework, LoggingSystem.get() returns this implementation, and setLogLevel updates the underlying java.util.logging.Logger instances accordingly.

What happens if I call setLogLevel with an invalid logger name?

If you provide a logger name that does not exist, the underlying logging framework typically creates a placeholder logger with that name and assigns the specified level. This logger will then capture log events if code later obtains a logger with the same name. Passing null specifically targets the root logger in all implementations.

Is it safe to change log levels frequently in a high-throughput production application?

Yes, the setLogLevel operation is lightweight and thread-safe in both Logback and Log4J2 implementations. The operation updates an atomic reference or volatile field internally, so frequent changes (e.g., triggered by monitoring alerts) do not significantly impact application performance. However, excessive logging at TRACE or DEBUG levels can cause I/O bottlenecks, so levels should be managed judiciously.

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 →