Dimitri De Franciscis

Freelance Java/CMS/Database consultant, blogger, guitar player, photography, rubik cuber.

Scopri chi sono

One of the most annoying problems of application deploying is how to correctly configure logging. In this article we'll see how Spring can help us in this area.

Absolute vs relative paths

The biggest issue of relative paths is that they must be manually modified for each application instance, thus requiring time and attention. Let's try to use relative paths instead.

Below you can see our first try, where we try to write on file (save this snippet in /WEB-INF/classes/log4j.properties):

log4j.rootLogger=ERROR, stdout, rollingFile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=WEB-INF/logs/application.log
log4j.appender.rollingFile.MaxFileSize=512KB
log4j.appender.rollingFile.MaxBackupIndex=10
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.rollingFile.Encoding=UTF-8

We choose to write logs to WEB-INF/logs/application.log, but when we try it (just deploy the web applican, add some test code for Log4j and start Tomcat) we can see that relative paths just didn't work! They have been created in TOMCAT_HOME/bin/WEB-INF/logs; this behavior is not acceptable for most of the environments, for obvious security and stability reasons.

Let's try also this (in log4j.properties):

log4j.appender.rollingFile.File=/WEB-INF/logs/application.log

(remember to add a slash "/" before "WEB-INF"), Log4j will try to write on C:WEB-INFlogs (Windows) or in /WEB-INF/logs (Linux/Unix). Clearly there's no easy and standard way to do configure relative paths for Log4j

Using absolute paths is a risky path: we would have to customize every installation, consuming potential development time. What can we do to fix this, then?

Spring comes to the rescue

As always Spring engineers thought about everything that could happen, and invented a really unobtrusive but still flexible way to overcome this difficulty: its name is Log4jWebConfigurer.

This class handles Log4j configuration and it was created to handle specifically the kind of problems we're analyzing today. It's main feature is quite simple: it makes available to configuration files a new variable, ${webapp.root}; this variable contains the absolute path in which our web application is installed. Our configuration will become then:

log4j.appender.rollingFile.File=${webapp.root}/WEB-INF/logs/application.log

Spring Configuration

As a first step we need the spring.jar archive on the classpath: just put it in /WEB-INF/lib and you're done. Let's configure now /WEB-INF/web.xml, adding these lines before <servlet> element:

<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

Unfortunately it seems that this solution doesn't work at all: tomcat will try to write logs in a directory named "${webapp.root}" (exactly as written)!! So we're not done, yet...

The problem is that Log4j takes control (in a reprehensible way I would say): it notices the presence of a log4j.properties file in the classpath, so it tries to load it automatically. It does so without waiting for Spring to give him instructions on how to do this, so it fails to interpret correctly the "${webapp.root}" variable. If you feel puzzled, you're not alone: I personally still feel puzzled today, after discovering myself this mechanism, it is just that crazy...

Anyway, Spring authors thought also about this, creating the context-param named log4jConfigLocation, let's see how to use it.

First of all, please change the name of log4j.properties file, otherwise Log4 will try to load it autonomously. Change it to something like log4j-myapp.properties. Then, add these lines to web.xml:

<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j-myapp.properties</param-value>
</context-param>

Et voilà: Log4j is bent to our will ;)

That's not all, folks...

Let's make ourselves a questions: what will happen if we have more than one web application that leverages this trick? Simple: a big mess! The fact is, ${webapp.root} is defined as a system property; Servlet specification in regard to this is not very clear, in theory servlet engine should isolate system property on a per-web application basis, but Tomcat doesn't do it.

I don't want to repeat myself too much, but... well, Spring has a solution for this, too...

We can define a new context-param:

<context-param>
<param-name>webAppRootKey</param-name>
<param-value>root-mia-istanza</param-value>
</context-param>

This parameter specifies the name of the system variable that will hold the web application root directory. So let's modify (the last time!) our configuration file:

log4j.appender.rollingFile.File=${root-mia-istanza}/WEB-INF/logs/application.log

Only caveat is to use different values for distinct web applications residing on the same servlet engine., to avoid conflicts (e.g. logs written only under the last loaded application).

Conclusions

As you can see there are problems, but solutions, too! Both Log4j and Spring have cons, but pros greatly overcome defects. I suggest not to be satisfied with trivial or less practical solutions, when your application deserves the best!

Linkography


In libreria

Se hai trovato utili le mie guide, o hai trovato particolarmente interessante o divertente un articolo, perché non offrirmi una birra? Con PayPal è molto semplice, basta cliccare sul pulsante qui sotto e seguire la procedura:

Sito ospitato presso Web4Web

Web4Web.IT - Low Cost Hosting