How to configure log4j for a web app
I often see environments where web applications use log4j for logging into files using various appenders. That is all well and good until I see that the logs are getting written into the application server’s logs. In JBOSS for example this is server.log. So why is this a bad idea ?
Why not to write into server.log:
- An application server’s log is supposed to be used by the app server and not by your application.
- This log is supposed to contain app server level information like loading war files / exceptions that were handed over to the container etc.
- Weeding through the logs of about 10 applications to find a particular debug / error line is going to be crazy.
- A PROD logging configuration should never have a console appender. The reason your application logs are getting logged into the server’s logs could be that your log4j configuration defines a console appender. This could mean you are logging the same thing in 2 places (assuming there is another file appender defined for your app).
Ideally each application should manage its own logging needs. Many developers seem to be unaware of a class named DomConfigurator in the log4j API that can load a log4j XML and configure it for your application. Here are the steps I usually take to prepare an application for logging
- Write a custom class that implements ServletContextListener like the one shown below. The class looks for a context param named log4jFileName that is defined in web.xml. This locates the log4j XML configuration file. This will configure log4j before other classes use it.
Custom listener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class StartupListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent arg0) { // Cleanup code goes here } @Override public void contextInitialized(ServletContextEvent sce) { Logger logger = null; ServletContext servletContext = sce.getServletContext(); String log4jFile = servletContext.getInitParameter("log4jFileName"); DOMConfigurator.configure(log4jFile); logger = LogManager.getLogger(StartupListener.class.getName()); logger.debug("Loaded: " + log4jFile); } } |
2. In your web.xml define the listener and a context param like so
Web.xml entries:
1 2 3 4 5 6 7 8 9 10 11 12 | <context-param> <param-name>log4jFileName</param-name> <param-value> /usr/local/app/log4j/config/log4jConfig.xml </param-value> </context-param> <listener> <listener-class> com.yourpackage.YourListener </listener-class> </listener> |
3. Now configure your log4jConfig.xml appropriately to ensure that your application logs are logged under an appropriate path. Use a file appender like RollingFileAppender to handle the retention policy for your applications.
Here is a sample log4j XML to help you
Sample log4j XML: (Customize as necessary)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?xml version="1.0" encoding="UTF-8"?> <log4j:configuration xmlns:log4j = "http://jakarta.apache.org/log4j/" debug = "false"> <appender name = "FILE" class = "org.apache.log4j.RollingFileAppender"> <param name = "File" value = "/usr/local/path/to/application.log"/> <param name = "MaxFileSize" value = "5MB"/> <param name = "MaxBackupIndex" value = "50"/> <layout class = "org.apache.log4j.PatternLayout"> <param name = "ConversionPattern" value = "[%d{dd/MM/yy hh:mm:ss:sss z}] %5p %c{2}: %m%n"/> </layout> </appender> <category name = "com.your.application.package"> <priority value = "debug"/> </category> <category name = "org.apache.catalina"> <priority value = "error"/> </category> <category name = "org.hibernate"> <priority value = "error"/> </category> <root> <priority value = "info"/> <appender-ref ref = "FILE"/> </root> </log4j:configuration> |
That is pretty much it. This will ensure that your application’s logs are logged elsewhere from the server’s own logs. Makes managing log files a little easier.
looks exactly like the stuff i recently blogged about:
http://blog.nofail.de/2010/04/taming-webapp-logging-with-log4j/
@phoet
Your method seems similar except for the fact that you use ‘configure and watch’ and do not use a context param. The JMX extension approach is interesting but is not practical. I would not expect logging to be a managed extension that can have changes at runtime.
Yes, we are talking about trying to improve the standard and thus about the same thing. But if you are implying that I tried to copy the idea of your standard, you are way off. I know several projects that use these standards before either of these blog entries were ever written.
Good suggestion.
Does this approach have advantages over using a custom init servlet, as described in the Ant 1.2 manual @ http://logging.apache.org/log4j/1.2/manual.html ?
@Vladimir Dzhuvinov
The Init servlet is also an option as well. It comes down to a matter of preference I guess. I usually choose a listener over an init servlet since I believe they are easier to spot.
@CertPal
Thank you. Today I implemented your suggestion my current web app project, a JSON-RPC service. Implementing the listener interface was simpler to code and also seems semantically more correct than using a custom init servlet.
@Vladimir Dzhuvinov
That’s great to hear ! Have a nice day.
no, that was not my intention. i just wanted to link to my post
@phoet
hahaha ! ok. The down vote plus the comment seemed like a subtle message to me.
Cheers