views:

8061

answers:

5

I have a java webapp that has to be deployed on either Win or Linux machines. I now want to add log4j for logging and I'd like to use a relative path for the log file as I don't want to change the file path on every deployment. The container will most likely be Tomcat but not necessarily.

What's the best way of doing this?

+9  A: 

Tomcat sets a catalina.home system property. You can use this in your log4j properties file. Something like this:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${catalina.home}/logs/LogFilename.log

If your using another container, try to find a similar system property, or define your own. Setting the system property will vary by platform, and container. But for Tomcat on Linux/Unix I would create a setenv.sh in the CATALINA_HOME/bin directory. It would contain:

export JAVA_OPTS="-Dcustom.logging.root=/var/log/webapps"

Then your log4j.properties would be:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log
Steve K
I honestly don't see what advantage this approach has over using the Listener I've explained in my answer.I don't care what container it is, it will just work no matter where I deploy it whereas in your approach I have to change a value if I change the environment.
Iker Jimenez
Both solutions are using system properties, we are just setting them differently. It really just depends, on flexibility versus simplicity. We manage all the Tomcat servers that our application runs on, so I like the flexibility. If your distributing a war for 3rd party use, simplicity makes sense
Steve K
+8  A: 

I've finally done it in this way.

Added a ServletContextListener that does the following:

public void contextInitialized(ServletContextEvent event) {
 ServletContext context = event.getServletContext();
 System.setProperty("rootPath", context.getRealPath("/"));
}

Then in the log4j.properties file:

log4j.appender.file.File=${rootPath}WEB-INF/logs/MyLog.log

By doing it in this way Log4j will write into the right folder as long as you don't use it before the "rootPath" system property has been set. This means that you cannot use it from the ServletContextListener itself but you should be able to use it from anywhere else in the app.

It should work on every web container and OS as it's not dependent on a container specific system property and it's not affected by OS specific path issues. Tested with Tomcat and Orion web containers and on Windows and Linux and it works fine so far.

What do you think?

Iker Jimenez
This is a good idea, but I think catalina.home might be safer to use as it will always be set/available before any log4j code initializes.
matt b
That would be true if I was only using Tomcat, but my requirement is that it has to work on any container with 0 configuration. My approach fulfills this and nobody has so far proposed a better approach for this.
Iker Jimenez
This looks like the best portable approach to me. The only thing better than simple configuration is no configuration at all.
Kyle W. Cartmell
This solution may work for Web applications that use Servlets, but Steve K's solution (http://stackoverflow.com/questions/216781/log4j-configuring-a-web-app-to-use-a-relative-path/216805#216805) works for any application that uses Log4j.
Derek Mahar
Spencer K's solution, also based on relative paths, works for any application that uses Log4j, assuming that you set the base directory to a predictable path.
Derek Mahar
As I said in the question, you cannot assume that we are deploying on a Tomcat here. Cannot use ${catalina.home} then.
Iker Jimenez
This solution only works if you have a single WebApp configured to use it because the System properties are global to tomcat a second app starting up would overwrite the value that the first webapp set. You could give each webapp a unique property name but if you are going to do that then you may as well just use ${catalina.home} and add the unique part of the path in the log4j.properties file as it is less error prone.
murdoch
+1  A: 

Doesn't log4j just use the application root directory if you don't specify a root directory in your FileAppender's path property? So you should just be able to use:

log4j.appender.file.File=logs/MyLog.log

It's been awhile since I've done Java web development, but this seems to be the most intuitive, and also doesn't collide with other unfortunately named logs writing to the ${catalina.home}/logs directory.

Spencer K
From what I've seen it can use the user's home directory, or the container's home directory if you don't give an absolute path.Unreliable.
Iker Jimenez
@Iker: Why can't you just explicitly set the application root directory in your container or application configuration? Once you've done that once in development and production, you can use relative paths reliably. Assuming the root directory is set correctly, relative paths are the most portable (re-locatable) solution.
Derek Mahar
+1  A: 

If you use Spring you can:

1) create a log4j configuration file, e.g. "/WEB-INF/classes/log4j-myapp.properties" DO NOT name it "log4j.properties"

Example:

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=${myWebapp-instance-root}/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'll define "myWebapp-instance-root" later on point (3)

2) Specify config location in web.xml:

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

3) Specify a unique variable name for your webapp's root, e.g. "myWebapp-instance-root"

<context-param>
  <param-name>webAppRootKey</param-name>
  <param-value>myWebapp-instance-root</param-value>
</context-param>

4) Add a Log4jConfigListener:

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

If you choose a different name, remember to change it in log4j-myapp.properties, too.

See my article (Italian only... but it should be understandable): http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

UPDATE (2009/08/01) I've translated my article to English: http://www.megadix.it/node/136

Megadix
+1  A: 

Just a comment on Iker's solution.

ServletContext is good solution for your problem. But I don't think it good for maintains. Most of time log files required to be saved.

Since ServletContext make the file under the deployed file, it will be removed when server got redeployed. Suggest go with rootPath's parent folder instead of child one.

lizi