Wednesday, July 1, 2009

Scheduler for Log4j configuration

In this blog, you can learn how to schedule a timer task to run at a certain time and repeatedly to configure the Log4j. We will go through the guidelines of the log4j first.
  • Use debug() logging for anything that is not necessarily a problem to be diagnosed
  • Use warn() for anything that is probably a problem and someone should look at it
  • Use error() for anything that is definitely a problem and someone should look at it
  • Use info() once in a blue moon for major events, like "Server now started up" or
    "Shutting down"

Usually we disable debug () logging on a production server, and want to spend time fixing/investigating any warn () or error () that turn up. We would not expect to have a lot of info (). Therefore, debug () logging is not intended for "postmortem" on production site errors; you need info/warn/error for that. debug() logging is intended to give some basic tracing of what's happening for developers as they work on their debug instance. The expectation is that when working on a bug or feature, a developer would add a bit more logging in the area of interest, but have the big picture already logged.

Suppose there is issue on the production server and you want to debug the logging of issue without redeploying the code on production. This we can achieve using the TimerTask and Timer. So let’s start the coding.

As per Log4j setting create the log4j.properties file in your workspace and keep in mind that we are not at all going to use it for log4j configuration. We will have another file in the working directory of the folder. Please go through the following properties file code which will be there in both locations.

log4j.rootLogger=INFO, MyLogFileAppender
log4j.appender.MyLogFileAppender=org.apache.log4j.DailyRollingFileAppender
log4j.appender.MyLogFileAppender.File=../logs/MyApp.log
log4j.appender.MyLogFileAppender.DatePattern=.yyyy-MM-dd
log4j.appender.MyLogFileAppender.Append=true
log4j.appender.MyLogFileAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.PersonalHealthLog.layout.ConversionPattern=%d{ISO8601} [%t] [%X{affiliateId}] %-5p %c{1} - %m%n
log4j.logger.com.search.services= DEBUG

Now consider that at the time of the production delivery you commented the following line of code and suppose issue has come into your code and you want to debug the logging. In that case, just uncomment the following code from your working directory log4j.properties file and you can view the log file for the issues.

# log4j.logger.com.search.services= DEBUG

The main question which might come in your mind is how we can configure the log4j runtime using working directory log4j properties file in fixed interval. So let’s go through the following code for the log4j configuration.

The Web.xml file settings are as follows….

The Java Servlet context listener is as follows…

public class Log4jConfigListener implements ServletContextListener {
private static final String PARAM_LOGGING_REFRESH_INTERVAL = "com.principal.health.member.LOGGING_REFRESH_INTERVAL";
private static final String PARAM_LOGGING_COLLECTION_NAME = "com.principal.health.member.LOGGING_COLLECTION_NAME";
private Log4jWatchdogTask log4jWatchdogTask = null;
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
String location = "file:./log4j.properties";
try {
// consider a plain file path as relative to the web application root directory.
if (!ResourceUtils.isUrl(location)) {
// Resolve system property placeholders before resolving real path.
location = SystemPropertyUtils.resolvePlaceholders(location);
location = WebUtils.getRealPath(servletContext, location);
} else {
location = SystemPropertyUtils.resolvePlaceholders(location);
}
URL url = ResourceUtils.getURL(location);
if (location.toLowerCase().endsWith(".xml")) {
DOMConfigurator.configure(url);
} else {
PropertyConfigurator.configure(url);
}
String refreshInterval = servletContext.getInitParameter(PARAM_LOGGING_REFRESH_INTERVAL);
if (refreshInterval != null) {

// interval assumed to be in minutes
int interval = Integer.parseInt(refreshInterval);
if (interval > 0) {
// Be sure Timer is a daemon so its Thread does not keep App Server running after shutdon
log4jWatchdogTask = new Log4jWatchdogTask(collectionName);
int intervalMillis = interval * 60 * 1000;
new Timer(true).schedule(log4jWatchdogTask, intervalMillis, intervalMillis);
} else {
servletContext.log("Invalid refresh interval [" + interval + "]. Number cannot be negative. Timer will not be scheduled.");
}
} catch (NumberFormatException e) {
servletContext.log("Invalid refresh interval [" + refreshInterval + "]. Must be a postive number. Timer will not be scheduled.");
}
} catch (FileNotFoundException e) {
servletContext.log("Log4j configuration failed! [" + location + "] file not found.");
}
}

Our Scheduler class…..

class Log4jWatchdogTask extends TimerTask {
private String collectionName;
public Log4jWatchdogTask(String collectionName) {
this.collectionName = collectionName;
}
public void run() {
String location = "file:./log4j.properties";
try {
// consider a plain file path as relative to the web application root directory.
if (!ResourceUtils.isUrl(location)) {
// Resolve system property placeholders before resolving real path.
location = SystemPropertyUtils.resolvePlaceholders(location);
location = WebUtils.getRealPath(servletContext, location);
} else {
location = SystemPropertyUtils.resolvePlaceholders(location);
}
URL url = ResourceUtils.getURL(location);
if (location.toLowerCase().endsWith(".xml")) {
DOMConfigurator.configure(url);
} else {
PropertyConfigurator.configure(url);
}
} catch (FileNotFoundException e) {
servletContext.log("Log4j configuration failed! [" + location + "] file not found.");
}
}
}