Dynamically Changing log4j log level
Asked Answered
P

10

144

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?

Pneumatics answered 4/1, 2011 at 21:38 Comment(4)
#1200949Callahan
Very similar to #2116400Persevering
Updated question for log4j2: #23434752Vasculum
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 #23434752 as @Vasculum recommends.Rostrum
L
95

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.

Linger answered 4/1, 2011 at 21:51 Comment(11)
But with this approach don't we have to compile and redeploy the app?Pneumatics
@Pneumatics No; you are grabbing the logger instance and setting it at runtime in the above codeLinger
For only runtime dependencies LogManager.getLogger(Class.forName("org.hibernate.util.JDBCExceptionReporter")).setLevel(Level.FATAL);Atthia
Note that the log4j 2 API does not provide a "setLevel" method.Mak
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? @AaronMcIverMatronymic
@VishalP If you are using the latest version of Log4j 2 you should be using implementation classes to solve this...Linger
@Mak Log4j 2 does provide a way to do this, although it's not as simple.Abbess
how does this compare to LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.DEBUG)?Haricot
will it applly to only the current classStutsman
Log4j2 can be configured to refresh its configuration by scanning the log4j2.xml file (or equivalent) at given intervals. E.g. <Configuration status="warn" monitorInterval="5" name="tryItApp" packages="">Naught
This answer should be downvoted, changed, removed or something. It only causes confusion as it doesn't document how to solve it for log4j2.Epiphany
D
98

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.

Dicks answered 4/1, 2011 at 22:22 Comment(6)
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 FAQShayneshays
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..!Molding
sorry I meant @PreDestroy methodMolding
"Log4j registers its loggers as JMX MBeans.". My servlet uses log4j 1.2 I don't see any log4j MBeans.Bravery
All you need to do is to invoke DOMConfigurator.configureAndWatch() once. How can I achieve this ?Scribe
"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.Epiphany
L
95

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.

Linger answered 4/1, 2011 at 21:51 Comment(11)
But with this approach don't we have to compile and redeploy the app?Pneumatics
@Pneumatics No; you are grabbing the logger instance and setting it at runtime in the above codeLinger
For only runtime dependencies LogManager.getLogger(Class.forName("org.hibernate.util.JDBCExceptionReporter")).setLevel(Level.FATAL);Atthia
Note that the log4j 2 API does not provide a "setLevel" method.Mak
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? @AaronMcIverMatronymic
@VishalP If you are using the latest version of Log4j 2 you should be using implementation classes to solve this...Linger
@Mak Log4j 2 does provide a way to do this, although it's not as simple.Abbess
how does this compare to LogManager.getLogManager().getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.DEBUG)?Haricot
will it applly to only the current classStutsman
Log4j2 can be configured to refresh its configuration by scanning the log4j2.xml file (or equivalent) at given intervals. E.g. <Configuration status="warn" monitorInterval="5" name="tryItApp" packages="">Naught
This answer should be downvoted, changed, removed or something. It only causes confusion as it doesn't document how to solve it for log4j2.Epiphany
N
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.

Naught answered 31/8, 2015 at 16:58 Comment(2)
@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.Naught
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?Guinna
Q
6

For log4j 2 API , you can use

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

Quicklime answered 27/4, 2020 at 12:6 Comment(0)
S
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>
Subfusc answered 5/5, 2016 at 17:16 Comment(2)
Whatever you mentioned seems to be fine, but the question is about dynamically changing the logging level.Undeniable
To do this, you need to restart the service. This does not change the logging levels dynamically.Fever
S
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.

Sesquipedalian answered 29/1, 2016 at 11:17 Comment(5)
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?Remains
I still don't get what you mean "on demand". Can you please show a snippet where you hook this method up?Remains
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);Sesquipedalian
So, "on demand" means "when the user clicks the button" in my caseSesquipedalian
Ah, got you. So your admin console has to make a request to invoke this method.Remains
T
1

The question breaks down into 2 parts:

  1. Where does the user input the logger and level? ie How do you capture the intent to change the log level?
  2. How do you broadcast this intent information to all relevant services?

Collecting the user intent comes in a few flavors:

  • An API request to change the levels eg POST /log-levels?logger=com.example.FooClass&level=DEBUG
  • A JMX Request
  • A Shared File
  • A Centralized UI (eg DIY, SpringBoot, Prefab)

Propagating the change can be done by:

  • Configuring scan / polling & NFS or remote URI
  • Service discovery and pushing out the change
  • Pub/Sub (Redis / SSE)
  • A log filter that looks in dynamic configuration to get the log level (the dynamic config system then uses something like pub sub).

A more detailed write-up in https://www.prefab.cloud/blog/dynamically-changing-java-log-level/ with an example of the filter approach and a look at centralized UIs.

As far as whether the changes will be permanent, that depends on which approach you take. If you PubSub or ServiceDiscovery, the change will only take effect until something restarts, because the new service won't get the message. You'll want something more like Kafka / Redis Streams if you want to be able to have new instance of your application get the current value of the log level. That's basically the dynamic configuration option.

Ton answered 8/9, 2023 at 16:17 Comment(0)
F
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);
    }
}
Fever answered 22/6, 2017 at 12:49 Comment(0)
A
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);
Accentuate answered 26/11, 2019 at 10:7 Comment(0)
B
-3

You can use following code snippet

((ch.qos.logback.classic.Logger)LoggerFactory.getLogger(packageName)).setLevel(ch.qos.logback.classic.Level.toLevel(logLevel));
Beautify answered 16/8, 2016 at 15:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.