Monday, February 15, 2010

Automatically reload log4j configuration in tomcat

I was looking for a way to have Log4j reload its configuration file automatically when it changes, in Tomcat. Log4j can do this with the configureAndWatch method, but the default initialization procedure (simply putting a log4j.properties file in the classpath) doesn't use configureAndWatch. You have to write at least a little bit of code to get Log4j to do this. I found the easiest solution for integration with Tomcat to be to implement a Tomcat LifecycleListener.

import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.log4j.LogManager;
import org.apache.log4j.PropertyConfigurator;

public class Log4JInitializer implements LifecycleListener
{
    private String propertiesFile;

    public String getPropertiesFile()
    {
        return this.propertiesFile;
    }

    public void setPropertiesFile(String propertiesFile)
    {
        this.propertiesFile = propertiesFile;
    }

    @Override
    public void lifecycleEvent(LifecycleEvent event)
    {
        if (Lifecycle.BEFORE_START_EVENT.equals(event.getType()))
            initializeLog4j();
    }

    private void initializeLog4j()
    {
        // configure from file, and let log4j monitor the file for changes
        PropertyConfigurator.configureAndWatch(propertiesFile);

        // shutdown log4j (and its monitor thread) on shutdown
        Runtime.getRuntime().addShutdownHook(new Thread()
        {
            @Override
            public void run()
            {
                LogManager.shutdown();
            }
        });
    }
}

I simply listen for the "BEFORE_START_EVENT", and if that happens (which is once per Tomcat startup) I initialize Log4j using the configureAndWatch method. I also don't forget to install a shutdown hook to cleanup the thread Log4j creates to poll the configuration file for changes (I could also have chosen to listen to the "AFTER_STOP_EVENT" from Tomcat in stead).

Package this in a jar, put it on the Tomcat classpath, and now you can configure it in your Tomcat serverl.xml.

<Server>
  ...
  <Listener className="Log4JInitializer" propertiesFile="/path/to/log4j.properties"/>
</Server>


Can't be much easier, and it does what it has to do.

5 comments:

Rafał Rusin said...

This is great. What is the license of this code? I'd like to use it.

Jan Van Besien said...

@Rafal
The license is "do whatever you want to do with it" ;-)

Rafał Rusin said...

Great! Thanks, I created repo and uploaded artifact. It's on Apache License now (so - do whatever you want :-) ).

http://github.com/rafalrusin/log4j-initializer

We could add configurable time inverval.

Lucas Theisen said...

I agree, this is a great approach. I already have a LifecycleListener to unload my DataSouce connection pool. Do you know if a log4j.properties dropped in the classpath will override this? I know tomcat will initialize log4j based upon the presence of a properties file in the classpath, but dont know if that will override if already configured?

Anonymous said...

Does the LogManager.shutdown() really kill the thread that configureAndWatch spawns (Daemon thread) ?

Any tips/tricks on how you would achieve startup/shutdown of log file configuration per webapp ?

cheers
Magnus