143

What are the different approaches for changing the log4j log level dynamically, so that I will not have to redeploy the application. Will the changes be permanent in those cases?

summerbulb
  • 5,709
  • 8
  • 37
  • 83
Ravi
  • 7,939
  • 14
  • 40
  • 43
  • 2
    http://stackoverflow.com/questions/1200449/how-do-i-adjust-log4j-levels-at-runtime – Cincinnati Joe Aug 09 '11 at 18:08
  • Very similar to http://stackoverflow.com/questions/2115900/is-it-possible-to-reload-log4j-xml-log4j-properties-file-dynamically-in-tomcat/ – vsingh Mar 20 '15 at 13:45
  • 4
    Updated question for log4j2: https://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2 – slaadvak Sep 04 '18 at 19:27
  • 2
    NOTE that (almost) everything on this page is about log4j, not log4j2. This whole page is so full of confusion and misdirection that it's USELESS. Go to https://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2 as @slaadvak recommends. – Lambart Feb 03 '20 at 17:50

9 Answers9

97

File Watchdog

Log4j is able to watch the log4j.xml file for configuration changes. If you change the log4j file, log4j will automatically refresh the log levels according to your changes. See the documentation of org.apache.log4j.xml.DOMConfigurator.configureAndWatch(String,long) for details. The default wait time between checks is 60 seconds. These changes would be persistent, since you directly change the configuration file on the filesystem. All you need to do is to invoke DOMConfigurator.configureAndWatch() once.

Caution: configureAndWatch method is unsafe for use in J2EE environments due to a Thread leak

JMX

Another way to set the log level (or reconfiguring in general) log4j is by using JMX. Log4j registers its loggers as JMX MBeans. Using the application servers MBeanServer consoles (or JDK's jconsole.exe) you can reconfigure each individual loggers. These changes are not persistent and would be reset to the config as set in the configuration file after you restart your application (server).

Self-Made

As described by Aaron, you can set the log level programmatically. You can implement it in your application in the way you would like it to happen. For example, you could have a GUI where the user or admin changes the log level and then call the setLevel() methods on the logger. Whether you persist the settings somewhere or not is up to you.

mhaller
  • 14,122
  • 1
  • 42
  • 61
  • 9
    A word of caution regarding log4j watchdog approach: "Because the configureAndWatch launches a separate watchdog thread, and because there is no way to stop this thread in log4j 1.2, the configureAndWatch method is unsafe for use in J2EE envrironments where applications are recycled". Reference: [Log4J FAQ](http://logging.apache.org/log4j/1.2/faq.html#a3.6) – Somu Nov 29 '11 at 19:29
  • If i was to use configureAndWatch functionality of Log4j I could stop that watchdog thread in an EJB's @PostDestroy method (that's a good enough indicator of when the container is shutting down) That's it ? Or is there more to it that I am missing..! – Robin Bajaj Sep 27 '13 at 20:08
  • sorry I meant @PreDestroy method – Robin Bajaj Sep 27 '13 at 20:14
  • "*Log4j registers its loggers as JMX MBeans.*". My servlet uses log4j 1.2 I don't see any log4j MBeans. – Abdull Jan 18 '16 at 13:39
  • **All you need to do is to invoke DOMConfigurator.configureAndWatch() once.** How can I achieve this ? – gstackoverflow Sep 05 '16 at 10:22
  • 2
    "As described by Aaron" - who is Aaron, and what is described? This answer needs to say whether there's a programmatic way to do this, and the answer seems to be NO. Please say so, as this is what you'd expect to exist, and there is no such solution apparently. – user239558 Mar 20 '18 at 10:46
96

Changing the log level is simple; modifying other portions of the configuration will pose a more in depth approach.

LogManager.getRootLogger().setLevel(Level.DEBUG);

The changes are permanent through the life cyle of the Logger. On reinitialization the configuration will be read and used as setting the level at runtime does not persist the level change.

UPDATE: If you are using Log4j 2 you should remove the calls to setLevel per the documentation as this can be achieved via implementation classes.

Calls to logger.setLevel() or similar methods are not supported in the API. Applications should remove these. Equivalent functionality is provided in the Log4j 2 implementation classes but may leave the application susceptible to changes in Log4j 2 internals.

Aaron McIver
  • 24,527
  • 5
  • 59
  • 88
  • But with this approach don't we have to compile and redeploy the app? – Ravi Jan 04 '11 at 21:53
  • @Ravi No; you are grabbing the logger instance and setting it at runtime in the above code – Aaron McIver Jan 05 '11 at 02:15
  • 7
    For only runtime dependencies `LogManager.getLogger(Class.forName("org.hibernate.util.JDBCExceptionReporter")).setLevel(Level.FATAL);` – CelinHC Oct 16 '11 at 18:38
  • 11
    Note that the log4j 2 API does not provide a "setLevel" method. – ChrisCantrell Oct 29 '13 at 20:36
  • 6
    But this only sets the root logger , isn't it? If individual levels are set for loggers under root, setting the root logger will have no effect on those LOGGER's. Wouldn't we have to do something like root.getLoggerRepository().getCurrentCategories(), iterate over each instance of the logger and set LEVELs for each logger? @AaronMcIver – TheMonkWhoSoldHisCode Jul 11 '14 at 09:17
  • @VishalP If you are using the latest version of Log4j 2 you should be using implementation classes to solve this... – Aaron McIver Jul 12 '14 at 17:50
  • 9
    @ChrisCantrell Log4j 2 [does provide a way to do this](http://stackoverflow.com/questions/23434252/programmatically-change-log-level-in-log4j2), although it's not as simple. – CorayThan Jul 21 '14 at 17:38
  • how does this compare to `LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.DEBUG)`? – Janus Troelsen Nov 28 '14 at 17:53
  • will it applly to only the current class – Amanuel Nega Jan 08 '15 at 09:23
  • 2
    Log4j2 can be configured to refresh its configuration by scanning the log4j2.xml file (or equivalent) at given intervals. E.g. – Kimball Robinson Aug 31 '15 at 16:56
  • 4
    This answer should be downvoted, changed, removed or something. It only causes confusion as it doesn't document how to solve it for log4j2. – user239558 Mar 20 '18 at 10:43
8

Log4j2 can be configured to refresh its configuration by scanning the log4j2.xml file (or equivalent) at given intervals. Just add the "monitorInterval" parameter to your configuration tag. See line 2 of the sample log4j2.xml file, which tells log4j to to re-scan its configuration if more than 5 seconds have passed since the last log event.

<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="warn" monitorInterval="5" name="tryItApp" packages="">

    <Appenders>
        <RollingFile name="MY_TRY_IT"
                     fileName="/var/log/tryIt.log"
                     filePattern="/var/log/tryIt-%i.log.gz">
            <Policies>
                <SizeBasedTriggeringPolicy size="25 MB"/>
            </Policies>
            ...
        </RollingFile>
    </Appenders>


    <Loggers>
        <Root level="error">
            <AppenderRef ref="MY_TRY_IT"/>
        </Root>
    </Loggers>

</Configuration>

There are extra steps to make this work if you are deploying to a tomcat instance, inside an IDE, or when using spring boot. That seems somewhat out of scope here and probably merits a separate question.

Kimball Robinson
  • 3,287
  • 9
  • 47
  • 59
  • @vsingh, are you deploying inside spring boot, tomcat, or a container or with an IDE? Sometimes there can be extra steps in those cases (not the fault of log4j2) -- basically the app can't see the file changing because it's been copied to another location by a framework or tool. – Kimball Robinson Feb 13 '19 at 18:06
  • Hi I tested this on Jboss6.4 and I updated the log4j2 config file under the location (inside of .war file) where app can see the file. However it still didn't work. Any suggestion? – Pupusa Feb 27 '19 at 17:14
6

For log4j 2 API , you can use

Logger logger = LogManager.getRootLogger();
Configurator.setAllLevels(logger.getName(), Level.getLevel(level));

mohamed stitane
  • 457
  • 1
  • 7
  • 12
4

This answer won't help you to change the logging level dynamically, you need to restart the service, if you are fine restarting the service, please use the below solution

I did this to Change log4j log level and it worked for me, I have n't referred any document. I used this system property value to set my logfile name. I used the same technique to set logging level as well, and it worked

passed this as JVM parameter (I use Java 1.7)

Sorry this won't dynamically change the logging level, it requires a restart of the service

java -Dlogging.level=DEBUG -cp xxxxxx.jar  xxxxx.java

in the log4j.properties file, I added this entry

log4j.rootLogger=${logging.level},file,stdout

I tried

 java -Dlogging.level=DEBUG -cp xxxxxx.jar  xxxxx.java
 java -Dlogging.level=INFO-cp xxxxxx.jar  xxxxx.java
 java -Dlogging.level=OFF -cp xxxxxx.jar  xxxxx.java

It all worked. hope this helps!

I have these following dependencies in my pom.xml

<dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
</dependency>

<dependency>
    <groupId>log4j</groupId>
    <artifactId>apache-log4j-extras</artifactId>
    <version>1.2.17</version>
</dependency>
Anandkumar
  • 1,338
  • 13
  • 15
  • 3
    Whatever you mentioned seems to be fine, but the question is about dynamically changing the logging level. – Azim Jul 13 '16 at 12:11
  • To do this, you need to restart the service. This does not change the logging levels dynamically. – kk. Jun 22 '17 at 10:42
2

With log4j 1.x I find the best way is to use a DOMConfigurator to submit one of a predefined set of XML log configurations (say, one for normal use and one for debugging).

Making use of these can be done with something like this:

  public static void reconfigurePredefined(String newLoggerConfigName) {
    String name = newLoggerConfigName.toLowerCase();
    if ("default".equals(name)) {
      name = "log4j.xml";
    } else {
      name = "log4j-" + name + ".xml";
    }

    if (Log4jReconfigurator.class.getResource("/" + name) != null) {
      String logConfigPath = Log4jReconfigurator.class.getResource("/" + name).getPath();
      logger.warn("Using log4j configuration: " + logConfigPath);
      try (InputStream defaultIs = Log4jReconfigurator.class.getResourceAsStream("/" + name)) {
        new DOMConfigurator().doConfigure(defaultIs, LogManager.getLoggerRepository());
      } catch (IOException e) {
        logger.error("Failed to reconfigure log4j configuration, could not find file " + logConfigPath + " on the classpath", e);
      } catch (FactoryConfigurationError e) {
        logger.error("Failed to reconfigure log4j configuration, could not load file " + logConfigPath, e);
      }
    } else {
      logger.error("Could not find log4j configuration file " + name + ".xml on classpath");
    }
  }

Just call this with the appropriate config name, and make sure that you put the templates on the classpath.

ISparkes
  • 1,695
  • 15
  • 15
  • what is the triggering point of this method? how does it know this method has to be invoked whenever there's a change in the log4j config? – asgs Mar 31 '16 at 20:24
  • I still don't get what you mean "on demand". Can you please show a snippet where you hook this method up? – asgs Apr 02 '16 at 05:42
  • 2
    Here you go: This is a snippet from the Controller where we use it. We have an admin page with some links to dynamically reconfigure logging, which then makes a GET to the link _/admin/setLogLevel?level=Error_ and then we catch this in the controller like this _@RequestMapping(value = "setLogLevel", method = RequestMethod.GET) public ModelAndView setLogLevel(@RequestParam(value = "level", required = true) String level) { Log4jReconfigurator.reconfigureExisting(level);_ – ISparkes Apr 05 '16 at 09:49
  • So, "on demand" means "when the user clicks the button" in my case – ISparkes Apr 05 '16 at 09:55
  • Ah, got you. So your admin console has to make a request to invoke this method. – asgs Apr 05 '16 at 10:41
0

If you would want to change the logging level of all the loggers use the below method. This will enumerate over all the loggers and change the logging level to given level. Please make sure that you DO NOT have log4j.appender.loggerName.Threshold=DEBUG property set in your log4j.properties file.

public static void changeLogLevel(Level level) {
    Enumeration<?> loggers = LogManager.getCurrentLoggers();
    while(loggers.hasMoreElements()) {
        Logger logger = (Logger) loggers.nextElement();
        logger.setLevel(level);
    }
}
kk.
  • 3,747
  • 12
  • 36
  • 67
0

I have used this method with success to reduce the verbosity of the "org.apache.http" logs:

ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("org.apache.http");
logger.setLevel(Level.TRACE);
logger.setAdditive(false);
-3

You can use following code snippet

((ch.qos.logback.classic.Logger)LoggerFactory.getLogger(packageName)).setLevel(ch.qos.logback.classic.Level.toLevel(logLevel));