149

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.

CorayThan
  • 17,174
  • 28
  • 113
  • 161
  • 2
    If you dont get an answer here try the mail list, its generally looked in to once in 2 days by the main authors. Then come back and answer your own question :-) – tgkprog May 02 '14 at 18:04

9 Answers9

219

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.

slaadvak
  • 4,611
  • 1
  • 24
  • 34
  • 3
    Right and if you want to change for just a particular logger (of a class/ package) get the context of that logger, setLevel and updateLoggers. – tgkprog May 02 '14 at 20:21
  • 65
    Such a convoluted way just to set the logging level. I'm sure there's a reason for doing it with five lines of code rather than the original one line in previous versions of log4j, but I just don't see it. In any case, thanks for this, @slaadvak! – Sturm May 23 '14 at 17:47
  • 2
    .updateLoggers() does not appear to be necessary. It seems that the changes done with .setLevel() are applied immediately. – zbyszek Jun 08 '15 at 17:52
  • above statement is half true/false, updateLoggers is needed when you already logged something using the logger you are changing, if you never logged anything updateLogger isn't needed. – Tinus Tate Apr 30 '16 at 10:07
  • 1
    The call to updateLoggers is always required, even if you add a new LoggerConfig. UpdateLoggers causes all the loggers to reaasociate themselves with LoggerConfigs and to change their log level to that of their associated LoggerConfig. If you add a new LoggerConfig any Loggers that match the new LoggerConfig pattern will be redirected to it. The "convoluted" way is required because Loggers and their configuration have been separated in Log4j2. – rgoers May 01 '16 at 14:56
  • Thanks! I am working on some standalone code before it gets integrated into our main codebase, and I just want all the damned logging to go to the console. Should be a common enough use case. I think needing 5 lines of code to do it is a bit excessive, but at least it works. – Lambart May 13 '16 at 18:15
  • 1
    Here is an answer that provides an updated, 1- or 2-line solution for newer versions of Log4J: https://stackoverflow.com/a/44678752/1339923 – Lambart Jul 11 '17 at 18:08
  • I've edited the answer to add some clarification around the Configurator class. – slaadvak Jul 12 '17 at 15:53
  • 1
    One thing to be clear about when @slaadvak mentions this is not part of the public api, you should be careful using this as a general solution as log4j could change the method signature in a newer version. Unless you wrap this in a util class, I only recommend using this solution as a temporary fix until you find a more general solution to silence noisy logs. Perhaps better log4j configuration management for each app. – Brian Olsen Feb 18 '20 at 16:15
  • Once it reaches a decade or so in the wild, you can bet your guns they consider this a "public API". Use the one-liner. Move on. – Robert Jan 10 '22 at 20:11
  • Make sure you don't do what I did - tried all the ways mentioned on this page, none worked, then realised I had this in my log4j2.xml: `` - you can't override that! (Just removed the level attribute and then `Configurator.setRootLevel(Level.TRACE)` worked) – Dave Griffiths May 18 '23 at 10:08
70

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);
a113nw
  • 1,312
  • 1
  • 16
  • 26
  • 4
    Got my vote because you pulled the logger names from the loggers themselves rather than hard coding the name as a string. – DWoldrich Oct 29 '19 at 18:45
  • 1
    Though this solution worked for console logging, for rollingFile usage, I had to set proper ThresholdFilter value to make sure the change was reflecting. – giri-sh Apr 24 '21 at 07:37
  • @giri-sh can you elaborate a bit? Maybe with your own answer with some code? I'm struggling as well... – Matthieu Feb 10 '22 at 10:36
  • 2
    @Matthieu, Add the threshold filter levels in rolling file config. For example, if you are using yaml config then set `ThresholdFilter: level: TRACE` in your log config yaml file. – giri-sh Feb 11 '22 at 04:50
19

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);
alfred.schalk
  • 211
  • 2
  • 4
  • 2
    This answer shows the same solution, as well as how to set it for the root logger--which is sometimes useful: https://stackoverflow.com/a/44678752/1339923 – Lambart Jul 11 '17 at 18:06
18

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();
}
Jörg Friedrich
  • 181
  • 1
  • 4
  • 3
    This didn't affect my logger at all. I used `setLevel(logger, Level.ERROR);` and logger.debug statements still printed. My log4j2.xml file is at http://pastebin.com/fcbV2mTW – Noumenon Aug 19 '15 at 01:09
  • I updated the code. Let me know if there are any issues with it. – Jörg Friedrich Sep 06 '16 at 21:18
  • 4
    In log4j 2.7 LoggerContext hasn't a getConfiguration() method, see http://logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/apache/logging/log4j/spi/LoggerContext.html – maxxyme Nov 23 '16 at 13:53
  • log4j-core-2.7.jar has and can be used as final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration configuration = ctx.getConfiguration(); – jprism Aug 28 '17 at 16:40
  • 7
    After seeing this, I am thinking.. "Maybe they do not want us to change the level in runtime?" – Koray Tugay Oct 23 '17 at 19:04
5

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>
hobgoblin
  • 865
  • 1
  • 10
  • 18
4

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

Victor
  • 2,450
  • 2
  • 23
  • 54
  • 1
    How is starting a client connecting to a JMX server just to change a log level *less* intrusive than calling a few methods? – Matthieu Feb 10 '22 at 10:40
  • Calling a few methods, from where? Code that is error prone and you have to maintain? Checking and modifying parameters/metrics at runtime is done externally in any serious application (take Spring Boot Actuator as an example). You rely on proven solutions to do such things, you don't reinvent the wheel. DRY/KISS – Victor Feb 24 '22 at 15:39
3

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.

markthegrea
  • 3,731
  • 7
  • 55
  • 78
2

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));
dpp.2325
  • 131
  • 1
  • 3
-2

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

Rhineb
  • 305
  • 3
  • 12
  • 1
    The question asked how to change log level in Logj42 Core, your answer shows how to change log level in Logback. – Piotr P. Karwasz Feb 11 '23 at 22:51
  • Your solution might be valid [for this question](https://stackoverflow.com/a/75423281/11748454), but in this context it just does not compile. The downvoter probably shared my opinion. – Piotr P. Karwasz May 05 '23 at 22:37