160

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.

1
  • 3
    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 :-) Commented May 2, 2014 at 18:04

11 Answers 11

234

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.

Sign up to request clarification or add additional context in comments.

12 Comments

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.
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!
.updateLoggers() does not appear to be necessary. It seems that the changes done with .setLevel() are applied immediately.
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.
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.
|
77

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); 

5 Comments

Got my vote because you pulled the logger names from the loggers themselves rather than hard coding the name as a string.
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 can you elaborate a bit? Maybe with your own answer with some code? I'm struggling as well...
@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.
Looking at the implementation of setAllLevels() it's literally the same as the other lengthy solutions.
20

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); 

1 Comment

This answer shows the same solution, as well as how to set it for the root logger--which is sometimes useful: stackoverflow.com/a/44678752/1339923
19

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(); } 

5 Comments

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 pastebin.com/fcbV2mTW
I updated the code. Let me know if there are any issues with it.
In log4j 2.7 LoggerContext hasn't a getConfiguration() method, see logging.apache.org/log4j/2.x/log4j-api/apidocs/index.html?org/…
log4j-core-2.7.jar has and can be used as final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); final Configuration configuration = ctx.getConfiguration();
After seeing this, I am thinking.. "Maybe they do not want us to change the level in runtime?"
8

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(); if (parentConfig != null) { 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> 

Comments

7

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.

Comments

2

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

3 Comments

How is starting a client connecting to a JMX server just to change a log level less intrusive than calling a few methods?
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
If you already have JMX configured and it is secured and you are already using JMX, this is reasonable. However, if you are not already using JMX, or you do not have your JMX configuration secured, this is a bad idea.
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)); 

Comments

0

This is working for me. My log level is info and its changed to debug.

 <Logger name="com.frequentisdefense.digitaltower" level="info" additivity="false"> changeLoggingLevel(Level.DEBUG); private static void changeLoggingLevel(Level newLevel) { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); // You could also specify the actual logger name as below and it will return the LoggerConfig used by the Logger. LoggerConfig loggerConfig = config.getLoggerConfig("com.frequentisdefense.digitaltower"); loggerConfig.setLevel(newLevel); ctx.updateLoggers(); } 

Comments

0

In my case, Configurator.setLevel(LogManager.getLogger(loggerName), Level.OFF) would almost never work. In most cases, the logger would remain enabled, but sometimes (on rare occasions) it would be disabled. Also, when I stopped execution on a breakpoint (before changing the level) in the debugger, it would be guaranteed to work.

It seems that there is some kind of a race and the standard initialization (from an XML config) happens concurrently with the code in which I try to set the log level.

In my case, this happens in tests, so I solved the problem with the following crutch:

private static final String INIT_COMPLETE_LOGGER = "InitComplete"; public static void waitTillConfigured() { boolean configured = waitForCondition(() -> { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); LoggerConfig loggerConfig = config.getLoggerConfig(INIT_COMPLETE_LOGGER); // If the logger we get by name has the given name and not the name of its parent, then it has its own configuration, so the configuration is finished. return INIT_COMPLETE_LOGGER.equals(loggerConfig.getName()); }, SECONDS.toMillis(10)); assertTrue( configured, "Log4j standard configuration was not completed in time; is there a logger called " + INIT_COMPLETE_LOGGER + "?" ); } 

and there is the following snippet in log4j2-test.xml:

<Logger name="InitComplete" level="OFF"> </Logger> 

The idea is that we just wait till InitComplete 'logger' actually gets configured; this (probably) means that the standard background log4j configuration routine is complete and we can proceed with our own programmatic configuration.

In my tests, I just invoke waitTillConfigured() before doing programmatic level configuration.

Log4j version is 2.24.3.

Comments

-4

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

2 Comments

The question asked how to change log level in Logj42 Core, your answer shows how to change log level in Logback.
Your solution might be valid for this question, but in this context it just does not compile. The downvoter probably shared my opinion.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.