Changing Log4j logging levels dynamically

Simple problem and may seem oh-not-so-cool. Make the log4j level dynamically configurable. You should be a able to change from DEBUG to INFO or any of the others. All this in a running application server.

First the simple, but not so elegant approach. Don't get me wrong (about the elegance statement) this approach works.

Log4j API
Often applications will have custom log4j properties files. Here we define the appenders and the layouts for the appenders. Somewhere in the java code we have to initialize log4j and point it to this properties file. We can use the following API call to configure and apply the dynamic update.
org.apache.log4j.PropertyConfigurator.configureAndWatch(
logFilePath,
logFileWatchDelay);
  • Pass it the path to the custom log4j.properties and a delay in milliseconds. Log4j will periodically check the file for changes (after passage of the configured delay time).
Spring Helpers
If you are using Spring then you are in luck. Spring provides ready-to-use classes to do this job. You can use the support class org.springframework.web.util.Log4jWebConfigurer. Provide it values for log4jConfigLocation, log4jRefreshInterval. For the path you can pass either one that is relative to your web application (this means you need to deploy in expanded WAR form) or provide an absolute path. I prefer the latter; that way I can keep my WAR file warred and not expanded.

There is also a web application listener class org.springframework.web.util.Log4jConfigListener that you can use in the web.xml file. The actual implementation of the Spring class Log4jWebConfigurer does the call to either:
org.apache.log4j.PropertyConfigurator.configureAndWatch 
OR
org.apache.log4j.xml.DOMConfigurator.configureAndWatch

Log4j spawns a separate thread to watch the file. Make sure your application has a shutdown hook where you can org.apache.log4j.LogManager.shutdown() to shut down log4j cleanly. The thread unfortunately does not die if your application is undeployed. Thats the only downside of using Log4j configureAndWatch API. In most cases thats not a big deal so I think its fine.

JMX Approach
JMX according to me is the cleanest approach. Involves some leg work initially but is well worth it. This example here is run on JBoss 4.0.5. Lets look at a simple class that will actually change the log level.

package com.aver.logging;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class Log4jLevelChanger {
public void setLogLevel(String loggerName, String level) {
if ("debug".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.DEBUG);
} else if ("info".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.INFO);
} else if ("error".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.ERROR);
} else if ("fatal".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.FATAL);
} else if ("warn".equalsIgnoreCase(level)) {
Logger.getLogger(loggerName).setLevel(Level.WARN);
}
}
}
  • Given a logger name and a level to change to this code will do just that. The code needs some error handling and can be cleaned up a little. But this works for what I am showing.
  • To change the log level we get the logger for the specified loggerName and change to the new level.
My application uses Spring so the rest of the configuration is Spring related. Now we need to register this bean as an MBean into the MBeanServer running inside JBoss. Here is the Spring configuration.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean id="exporter"
class="org.springframework.jmx.export.MBeanExporter"
lazy-init="false">
<property name="beans">
<map>
<entry key="bean:name=Log4jLevelChanger"
value-ref="com.aver.logging.Log4jLevelChanger" />
</map>
</property>
</bean>

<bean id="com.aver.logging.Log4jLevelChanger"
class="com.aver.logging.Log4jLevelChanger">
</bean>

</beans>
  • In Spring we use the MBeanExporter to register your MBeans with the containers running MBean Server. 
  • I provide MBeanExporter with references to beans that I want to expose via JMX.
  • Finally my management bean is Log4jLevelChanger is registered with Spring.
Thats it. With this configuration your bean will get registered into JBoss's MBean server. By default Spring will publish all public methods on the bean via JMX. If you need more control on what methods get published then refer to Spring documentation. I will probably cover that topic in a separate blog since I had to do all of that when i set up JMX for a project using Weblogic 8.1. With Weblogic 8.1 things are unfortunately not that straight forward as above. Thats for another day another blog.

One thing to note here is that the parameter names are p1 (for loggerName) and p2 for (level). This is because I have not provided any meta data about the parameters. When I do my blog on using JMX+Spring+CommonsAttributes under Weblogic 8.1, you will see how this can be resolved. BTW for jdk 1.4 based Spring projects you must use commons attributes tags provided by Spring to register and describe your beans as JMX beans. The initial minor learning curve will save you tons of time later.



 del.icio.us  Stumbleupon  Technorati  Digg 

 

What did you think of this article?




Trackbacks
  • No trackbacks exist for this entry.
Comments

  • 12/15/2006 2:14 AM Ian Lim wrote:
    Weblogic provided a jsp page known as Log4jAdmin which we used with very good results for this purpose.
    Reply to this
    1. 12/15/2006 7:27 AM Mathew Thomas wrote:
      I considered this exact same approach but did not realize there was a ready-made page available at the dev2dev site. For a lot of projects that may be just about good enough. The advantage with JMX is that you can have operations folks who are using a JMX-supporting management tool (such as openview or jmanage) to control certain behaviour from the tool rather than visit the individual applications page (in this case log4j being just one of the options).

      Thanks for pointing me to the dev2dev posted component.

      Reply to this
      1. 5/8/2007 5:34 AM Kitty wrote:
        Hi,

        Really helpful article,got me reading on mbeans and spring.. and since BEA does not recommend using deamon threads and configureAndWatch is just that.

        But it would be more helpful if you could provide the weblogic 8.1 mbean article too

        Cheers,
        kitty
        Reply to this
  • 9/11/2007 10:37 AM Pavan wrote:
    Very useful information. Thanks.
    In order to change for whole application you can use
    Level lvl = Level.toLevel(level);
    Logger.getRootLogger().setLevel(lvl);
    This avoids having sending the loggerName.
    Reply to this
  • 7/7/2008 2:59 AM sharfah wrote:
    Great example! Here is one that shows what to do if you're not using Spring:

    http://fahdshariff.blogspot.com/2008/06/change-logging-levels-using-jmx-howto.html
    Reply to this
Leave a comment

Submitted comments will be subject to moderation before being displayed.

 Enter the above security code (required)

 Name

 Email (will not be published)

 Website

Your comment is 0 characters limited to 3000 characters.