Channels ▼
RSS

JVM Languages

Custom Configuring Java Apps: Extending log4j


Last month, I introduced the notion of using a Java enum to solve the location-based configuration problem. This article continues that discussion, and also puts last month's class to use by building a utility class that I'll be using in future articles: an extended version of a log4j Logger that automatically configures itself and adds printf()-like logging methods to the standard Logger. Along the way, I'll discuss several design issues that should be of interest, even if you're not building your own loggers. I'll be using Apache Tomcat for this exposition.

How (and When) Do You Find a Configuration File?

Server-side log4j configuration is tough to do right. Log4j's configuration problems are far from rare, however, so it's a good platform for discussing the configuration issue as a whole.

By default, log4j looks for its configuration file (either log4j.properties or log4j.xml) in two places:

  1. The directory (or URL) specified in the System property log4j.configuration, either created in your program using System.setProperty("log4j.configuration","/file/location") or specified with the java -Dlog4j.configuration=/file/location command-line argument.
  2. On the classpath (as defined by whatever class loader pulled log4j into the program. In Tomcat, this is the WEB-INF/classes directory in your WAR file, not the CLASSPATH environment). Other servers have other rules.

In the case of Tomcat, -D on the command line does work, but you will have to modify your out-of-the-box startup script to handle it. You can also put a -D argument into the TOMCAT_OPTS environment variable. If neither of the automatic approaches work, you can configure the system by writing your own code to do it. A servlet, for example, could configure log4j in the HttpServlet's init() method or in a ServletContextListener object. We'll look at an example in a moment.

There are show-stopper problems with all these possibilities, however, particularly with Web servers such as Tomcat.

First, a given Web server typically hosts many Web applications, and these applications typically have very different requirements for logging configuration (at minimum, each application will probably want its own, uniquely named, log file), so using -D won't work because the resulting value is global across all applications. The same issues apply to an environment variable.

Next, you have no control over the classpath — Tomcat effectively limits the classpath to your Web app's WAR file — so classpath-based configuration is unworkable.

A given application may be installed on many servers, and each of those servers might have different configuration requirements. You really don't want your installation process to require that you dig into an application's WAR file to modify logging configuration, and modifications you might make to automatically expanded WARs will be wiped out on the next deployment.

Configuration should happen outside the application entirely, so that you can define a unique version on each server without modifying the WAR.

Finally, configuration often fails because the bit of code that performs the configuration runs at the wrong time. For example, consider a static field in a servlet that's initialized at compile time:

private static myConstant = new ConstantClass();  
// constructor logs useful information!

If the constructor logs something, that logging call will most likely be done before your servlet's init() method or ServletContextListener object runs, so you're doomed if you're configuring log4j in either of those places. Log4j just shuts down (permanently) when you try to log before configuring the system, so this innocuous declaration effectively disables logging.

A static initializer block like

static
{
    org.apache.log4j.xml.DOMConfigurator
            .configure( "/path/to/log4j.xml" );
}

has the same problem. The configuration does happen before any instances of the class are created, but there's no telling when this code will execute relative to other static initializers in other class definitions, and what if one of those initializers reconfigures the system?

This order-of-initialization issue is a particular problem when multiple instances of your Web app are running simultaneously, which can happen when you're providing a customized service to several clients. You might deploy the same Web app many times, with slightly different names given to each WAR file, for example. Ideally, you'd pull configuration information out of a directory that was named after the URL used to access the application, but that's particularly tricky to do because the URL isn't available to a servlet until you actually receive a message. (It's in the HttpServletRequest object), which is way too late to be useful. The Web app (context) name is available in the ServletContextListener, however, so you can configure there; but this approach still doesn't solve the static initializer problem.

So, given that all the standard ways of doing things fail, we're left with the Places class that I presented last month, at least for finding a default log4j.properties file at a known location outside the WAR file. (I put the configuration file in Places.CONFIG.file("log4j.properties")).

We still have the static-initializer problem, however. That is, it simply shouldn't be possible to log a message unless log4j has been configured properly, even if we're logging from a static initializer. I've solved that problem by writing my own version of the Logger class (Listing One), which leverages the Singleton design pattern to guarantee configuration.

Now I'll demonstrate some of the approaches you can take to implement a self-configuring log4j Logger.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video