Here's a lesson learned from the "school of hard knocks"...
SUMMARY: If you use an initialization servlet to initialize log4j, you may have trouble using log4j in a ServletContextListener. Instead, initialize log4j by putting a log4j.properties file in WEB-INF/classes.
Here are details of the "hard knock"...
PROBLEM: My logger was not logging. I had created a log4j Logger as a static variable in a ServletContextListener. I called the logger's debug() method in the listener's contextInitialized() method. The debugging messages were not logged. Instead, these error messages appeared in the Tomcat log:
log4j:WARN No appenders could be found for logger (ServletContextListener1). log4j:WARN Please initialize the log4j system properly.
DIAGNOSIS: I was using an initialization servlet (named Log4jInit) to initialize log4j for the webapp. This is described in the log4j manual as "the most flexible way for initializing log4j." The idea is that the container loads the servlet when it starts up, and the code to initialize log4j can be placed in the servlet's init() method. However, Tomcat loaded the ServletContextListener before it loaded Log4jInit, so log4j was not configured in time for the ServletContextListener.
ACTION: Instead of using an initialization servlet, just create a log4j.properties file and place it in the WEB-INF/classes directory of the webapp. This is another technique suggested by the log4j manual, and much easier than using an initialization servlet! It seems that log4j is automatically initialized using this log4j.properties file, before the ServletContextListener is loaded.
RESULT: The log4j logger now works correctly when called from within the ServletContextListener's contextInitialized() method.
Now that is a good idea, John, and definately food for thought.
I would like to show my appreciation for this idea, by contributing to some additional food for thought.
To place the log4j initialization code into a file outside of the web application, and then use a JNDI environment variable in tomcat so that it is indirected from the web application, the log4j init gets the path to its config file as the value of a string environment variable. This saves me time when i have to build a war file, i can just move it from dev instance (debug on) to the prod instance (debug off), since the logging, (among other things) is now configured in a server instance, and not the web app module. I guess it is what JNDI DataSources are to database connections, for the log4j configuration.
I had been doing this with a log4j init servlet for a while now, and was just putting up with the logging not working until the app got booted up, it was just not obvious to me that this should be in a context listener.
but now here is it in a servlet context listener. It is all jndi and log4j api stuff, so was pretty quick to move over.
to make this work in tomcat, i add the following to the web.xml:
and to the META-INF/context.xml file, for tomcat to auto map JNDI resorces on deployment of a war:
and in the tomcat instance's server.xml:
where the value of the environment variable (file:/...) is the full property path to the log4j initialization file, outside of your web application. For me, this is also my system configuration for the tomcat instance folder.
Error: Keyboard not attached. Press F1 to continue.
Paper jam tastes about as you would expect. Try some on this tiny ad: