Question

I'm interested in programmatically changing the log level in Log4j2. I tried looking at their configuration documentation but that didn't seem to have anything. I also tried looking in the package: org.apache.logging.log4j.core.config, but nothing in there looked helpful either.

Was it helpful?

Solution

The Easy Way :

EDITED according to log4j2 version 2.4 FAQ

You can set a logger’s level with the class Configurator from Log4j Core. BUT be aware that the Configurator class is not part of the public API.

// org.apache.logging.log4j.core.config.Configurator;
Configurator.setLevel("com.example.Foo", Level.DEBUG);

// You can also set the root logger:
Configurator.setRootLevel(Level.DEBUG);

Source

The Preferable Way :

EDITED to reflect changes in the API introduced in Log4j2 version 2.0.2

If you wish to change the root logger level, do something like this :

LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME); 
loggerConfig.setLevel(level);
ctx.updateLoggers();  // This causes all Loggers to refetch information from their LoggerConfig.

Here is the javadoc for LoggerConfig.

OTHER TIPS

The accepted answer by @slaadvak did not work for me for Log4j2 2.8.2. The following did.

To change the log Level universally use:

Configurator.setAllLevels(LogManager.getRootLogger().getName(), level);

To change the log Level for only the current class, use:

Configurator.setLevel(LogManager.getLogger(CallingClass.class).getName(), level);

I found a good answer here: https://garygregory.wordpress.com/2016/01/11/changing-log-levels-in-log4j2/

You can use org.apache.logging.log4j.core.config.Configurator to set the level for a specific logger.

Logger logger = LogManager.getLogger(Test.class);
Configurator.setLevel(logger.getName(), Level.DEBUG);

If you want to change a single specific logger level (not the root logger or loggers configured in the configuration file) you can do this:

public static void setLevel(Logger logger, Level level) {
    final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
    final Configuration config = ctx.getConfiguration();

    LoggerConfig loggerConfig = config.getLoggerConfig(logger.getName());
    LoggerConfig specificConfig = loggerConfig;

    // We need a specific configuration for this logger,
    // otherwise we would change the level of all other loggers
    // having the original configuration as parent as well

    if (!loggerConfig.getName().equals(logger.getName())) {
        specificConfig = new LoggerConfig(logger.getName(), level, true);
        specificConfig.setParent(loggerConfig);
        config.addLogger(logger.getName(), specificConfig);
    }
    specificConfig.setLevel(level);
    ctx.updateLoggers();
}

Most of the answers by default assume that logging has to be additive. But say that some package is generating lot of logs and you want to turn off logging for that particular logger only. Here is the code that I used to get it working

    public class LogConfigManager {

    public void setLogLevel(String loggerName, String level) {
        Level newLevel = Level.valueOf(level);
        LoggerContext logContext = (LoggerContext) LogManager.getContext(false);
        Configuration configuration = logContext.getConfiguration();
        LoggerConfig loggerConfig = configuration.getLoggerConfig(loggerName);
        // getLoggerConfig("a.b.c") could return logger for "a.b" if there is no logger for "a.b.c"
        if (loggerConfig.getName().equalsIgnoreCase(loggerName)) {
            loggerConfig.setLevel(newLevel);
            log.info("Changed logger level for {} to {} ", loggerName, newLevel);
        } else {
            // create a new config.
            loggerConfig = new LoggerConfig(loggerName, newLevel, false);
            log.info("Adding config for: {} with level: {}", loggerConfig, newLevel);
            configuration.addLogger(loggerName, loggerConfig);


            LoggerConfig parentConfig = loggerConfig.getParent();
            do {
                for (Map.Entry<String, Appender> entry : parentConfig.getAppenders().entrySet()) {
                    loggerConfig.addAppender(entry.getValue(), null, null);
                }
                parentConfig = parentConfig.getParent();
            } while (null != parentConfig && parentConfig.isAdditive());
        }
        logContext.updateLoggers();
    }
}

A test case for the same

public class LogConfigManagerTest {
    @Test
    public void testLogChange() throws IOException {
        LogConfigManager logConfigManager = new LogConfigManager();
        File file = new File("logs/server.log");
        Files.write(file.toPath(), new byte[0], StandardOpenOption.TRUNCATE_EXISTING);
        Logger logger = LoggerFactory.getLogger("a.b.c");
        logger.debug("Marvel-1");
        logConfigManager.setLogLevel("a.b.c", "debug");
        logger.debug("DC-1");
        // Parent logger level should remain same
        LoggerFactory.getLogger("a.b").debug("Marvel-2");
        logConfigManager.setLogLevel("a.b.c", "info");
        logger.debug("Marvel-3");
        // Flush everything
        LogManager.shutdown();

        String content = Files.readAllLines(file.toPath()).stream().reduce((s1, s2) -> s1 + "\t" + s2).orElse(null);
        Assert.assertEquals(content, "DC-1");
    }
}

Assuming following log4j2.xml is in classpath

<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">

    <Appenders>
        <File name="FILE" fileName="logs/server.log" append="true">
            <PatternLayout pattern="%m%n"/>
        </File>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout pattern="%m%n"/>
        </Console>
    </Appenders>

    <Loggers>
        <AsyncLogger name="a.b" level="info">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="FILE"/>
        </AsyncLogger>

        <AsyncRoot level="info">
            <AppenderRef ref="STDOUT"/>
        </AsyncRoot>
    </Loggers>

</Configuration>

The programmatic approach is rather intrusive. Perhaps you should check JMX support given by Log4J2:

  1. Enable the JMX port in your application start up:

    -Dcom.sun.management.jmxremote.port=[port_num]

  2. Use any of the available JMX clients (the JVM provides one in JAVA_HOME/bin/jconsole.exe) while executing your application.

  3. In JConsole look for the "org.apache.logging.log4j2.Loggers" bean

  4. Finally change the level of your logger

The thing that I like most of this is that you don´t have to modify your code or configuration for managing this. It´s all external and transparent.

More info: http://logging.apache.org/log4j/2.x/manual/jmx.html

For those of you still struggeling with this, I had to add the classloader to the "getContext()" call:

  log.info("Modifying Log level! (maybe)");
  LoggerContext ctx = (LoggerContext) LogManager.getContext(this.getClass().getClassLoader(), false);
  Configuration config = ctx.getConfiguration();
  LoggerConfig loggerConfig = config.getLoggerConfig("com.cat.barrel");
  loggerConfig.setLevel(org.apache.logging.log4j.Level.TRACE);
  ctx.updateLoggers();

I added a jvm argument: -Dlog4j.debug to my test. This does some verbose logging for log4j. I noticed that the final LogManager was not the one that I was using. Bam, add the class loader and you are off to the races.

One un-usual way i found to do is to create two separate file with different logging level.
For example. log4j2.xml and log4j-debug.xml Now change the configuration from this files.
Sample Code:

ConfigurationFactory configFactory = XmlConfigurationFactory.getInstance();
            ConfigurationFactory.setConfigurationFactory(configFactory);
            LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
            ClassLoader classloader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classloader.getResourceAsStream(logFileName);
            ConfigurationSource configurationSource = new ConfigurationSource(inputStream);

            ctx.start(configFactory.getConfiguration(ctx, configurationSource));

As of 2023 the above do not seem to work (or I will say at least did not seem to work for me), what did work was the following

import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;

final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
final Logger logger = loggerContext.exists(org.slf4j.Logger.ROOT_LOGGER_NAME); // give it your logger name
final Level newLevel = Level.toLevel("ERROR", null); // give it your log level
logger.setLevel(newLevel);

If you want to see how to do this on a per request basis see my comment on post Change priority level in log4j per request

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top