Initialize Log4J in a web application with a ServletContextListener

While LogJj will self-initialize if it finds a file called log4j.properties or log4j.xml in the classpath, it will only read the file once at application startup.

I’ve often had the need to be able to change the logging level in a long-running application, such as a web application, so that I can switch logging to debug level as needed without requiring a restart.

Log4J provides configurator classes that will monitor a configuration file for changes and reload the configuration when the file changes. The implementation appears to spawn a background thread that sleeps for a specified interval, then wakes up and checks the configuration file for changes.

You can use this method by calling PropertyConfigurator.contigureAndWatch() or DOMConfigurator.configureAndWatch() methods. The challenge is that you should only call these methods once when your application starts up as each call to the above methods will result in a new thread to monitor the configuration file.

In many applications, it is a simple matter to call these methods only once. The question often comes up as to where to make the call to a ConfigureAndWatch method from within a web application. A collegue of mine once accidentally put this call in the main execution path of a high-traffic web application and brought the site down as the web application container couldn’t manage all the resulting threads.

I’ve seen several methods of ensuring that the ConfigureAndWatch method is only called once from within a web application, but I think the cleanest and simplest is to create a Context Listener to initialize Log4J.

The following example creates a ServletContextListener which will initialize Log4J when the application is loaded by the container, and will shut down logging for the application when it is unloaded.

package some.package;

import java.io.File;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.LogManager;
import org.apache.log4j.xml.DOMConfigurator;
/**
 * ServletContextListener to initialize Logging for the application
 */

public class Log4jConfigListener implements ServletContextListener
{
    private static long DEFAULT_INTERVAL = 300000;
    public void contextDestroyed(ServletContextEvent event)
    {
        //Turn off logging when the Servlet Context is destroyed
    	LogManager.shutdown();
    }

public void contextInitialized(ServletContextEvent event)
    {
        //Read configuration properties from web.xml
        String file = event.getServletContext().getInitParameter("LOG4J_CONFIG_LOCATION");
        String interval = event.getServletContext().getInitParameter("LOG4J_REFRESH_INTERVAL");
        long delay = DEFAULT_INTERVAL; //Default delay is 5 minutes

//Check for interval parameter
        if (interval != null && !"".equals(interval))
        {
            try
            {
                delay = Long.parseLong(interval);
            }
            catch(NumberFormatException e)
            {
                //Can't really log the error since we haven't initialized Log4J
                //Will use the default value
                delay = DEFAULT_INTERVAL;
            }
        }

//Check for file parameter
        if(file != null && !"".equals(file))
        {
            if(!(new File(file).exists()))
            {
                throw new IllegalArgumentException("Invalid 'LOG4J_CONFIG_LOCATION' parameter value '"
                                                                 + file + "'.");
            }
if(file.toLowerCase().endsWith(".xml"))
            {
                DOMConfigurator.configureAndWatch(file, delay);
            }
            else
            {
                PropertyConfigurator.configureAndWatch(file, delay);
            }
        }
    }
}

Once you add this class to your application, you need to configure the listener. This is done by adding the following to your web.xml file:

<!-- Log4j Initialization Parameters -->
<context-param>
  <!-- absolute path to log4j config file (*.properties or *.xml) -->
<param-name>LOG4J_CONFIG_LOCATION</param-name>
<param-value>
  ABSOLUTE_PATH_TO_YOUR_LOG4J_CONFIGURATION_FILE
  </param-value>
</context-param>
<context-param>
  <!-- number of milliseconds to wait before checking LOG4J_CONFIG_LOCATION for updates -->
  <param-name>LOG4J_REFRESH_INTERVAL</param-name>
  <param-value>(e.g. 300000)</param-value>
</context-param>

<listener>
  <listener-class>
    some.package.Log4jConfigListener
  </listener-class>
</listener>

4 thoughts on “Initialize Log4J in a web application with a ServletContextListener”

  1. Yes, indeed it is!

    Honestly speaking, I had to puzzle together nearly the same solution for exactly the same problem, before I found this post ;-), but I’m still happy finding a confirmation that I am not the only one going this way.

  2. Thanks for a nice article, I think i have found a way to config log4j in my later project!!!

Leave a Reply

Your email address will not be published. Required fields are marked *