Last month, I introduced the notion of using a Java enumLoggerprintf()Logger
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:
- The directory (or URL) specified in the
propertySystemlog4j.configuration, either created in your program using or specified with theSystem.setProperty("log4j.configuration","/file/location")java -Dlog4j.configuration=/file/locationcommand-line argument. - 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_OPTSHttpServletinit()ServletContextListener
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
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()ServletContextListener
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
HttpServletRequestServletContextListener
So, given that all the standard ways of doing things fail, we're left with the PlacesPlaces.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


