Friday, May 02, 2008

Reconfigure a running log4j configuration

If you analyse a problem you frequently want to change the log level of a running application server. This chapter explains how you can do this. I used Tomcat as example server but you can use any application server you like.

The XML actually offers a method to watch changes in config files.

http://logging.apache.org/log4j/docs/api/org/apache/log4j/xml/DOMConfigurator.html#configureAndWatch(java.lang.String)

The problem is that it seems not to work in some situations. But this is no problem as it is quite easy to develop a short tool by yourself. We have two options. We could change the log level during runtime:

Logger root = Logger.getRootLogger();
root.setLevel(Level.WARN);

or we can reload the configuration:

// PropertyConfigurator.configure(url);
DOMConfigurator.configure(url);

The following example will check the configuration file in defined intervals and reconfigure log4j if any changes are found.

We need to create three things:

a) a monitor thread, monitoring the configuration file and reconfiguring log4j if needed

b) a servlet starting and stopping the monitor thread

c) an entry in the web.xml, to initialize the servlet

The following class monitors the logj4 configuration file and checks with the last change date has changed:

package de.laliluna.logexample;

import java.io.File;
import java.net.URL;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.xml.DOMConfigurator;

public class MonitorThread implements Runnable {

private static Logger log = Logger.getLogger(MonitorThread.class);

boolean interruped;

private long checkIntervalMillis = 10000;

private URL url;

private File file;

// stores the last modification time of the file
private long lastModified = 0;

public void run() {
System.out.println("Initialize " + url.getPath());
file = new File(url.getPath());
// PropertyConfigurator.configure(url);
DOMConfigurator.configure(url);
lastModified = file.lastModified();

monitor();
}

private void monitor() {
log.info("Starting log4j monitor");

while (!interruped) {

// check if File changed
long temp = file.lastModified();
if (lastModified != temp) {
log.info("Initialize log4j configuration " + url.getPath());
// PropertyConfigurator.configure(url);
DOMConfigurator.configure(url);

lastModified = temp;

} else
log.debug("Log4j configuration is not modified");
try {
Thread.currentThread().sleep(checkIntervalMillis);
} catch (InterruptedException e) {
interruped = true;
}
}
log.info("Shutting down log4j monitor");

}

public URL getUrl() {
return url;
}

public void setUrl(URL url) {
this.url = url;
}

public long getCheckIntervalMillis() {
return checkIntervalMillis;
}

/**
* Sets the interval for checking the url for changes. Unit is
* milliseconds, 10000 = 10 seconds
*
* @param checkIntervalMillis
*/
public void setCheckIntervalMillis(long checkIntervalMillis) {
this.checkIntervalMillis = checkIntervalMillis;
}

public boolean isInterruped() {
return interruped;
}

public void setInterruped(boolean interruped) {
this.interruped = interruped;
}

}

The servlet starts and stops the monitor thread:

package de.laliluna.logexample;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

public class Log4jConfigLoader extends HttpServlet {

private Thread thread;

@Override
public void destroy() {
thread.interrupt();
super.destroy();
}

public void init() throws ServletException {
super.init();
MonitorThread monitorThread = new MonitorThread();
monitorThread.setCheckIntervalMillis(10000);
monitorThread.setUrl(Log4jConfigLoader.class.getResource("/log4j.xml"));
thread = new Thread(monitorThread);
thread.start();
}

}

We add the servlet to the web.xml to initialize it.



xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

log4j-init
de.laliluna.logexample.Log4jConfigLoader
10




ref

No comments: