import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleListener; import org.apache.log4j.LogManager; import org.apache.log4j.PropertyConfigurator; public class Log4JInitializer implements LifecycleListener { private String propertiesFile; public String getPropertiesFile() { return this.propertiesFile; } public void setPropertiesFile(String propertiesFile) { this.propertiesFile = propertiesFile; } @Override public void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.BEFORE_START_EVENT.equals(event.getType())) initializeLog4j(); } private void initializeLog4j() { // configure from file, and let log4j monitor the file for changes PropertyConfigurator.configureAndWatch(propertiesFile); // shutdown log4j (and its monitor thread) on shutdown Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { LogManager.shutdown(); } }); } }
I simply listen for the "BEFORE_START_EVENT", and if that happens (which is once per Tomcat startup) I initialize Log4j using the configureAndWatch method. I also don't forget to install a shutdown hook to cleanup the thread Log4j creates to poll the configuration file for changes (I could also have chosen to listen to the "AFTER_STOP_EVENT" from Tomcat in stead).
Package this in a jar, put it on the Tomcat classpath, and now you can configure it in your Tomcat serverl.xml.
<Server> ... <Listener className="Log4JInitializer" propertiesFile="/path/to/log4j.properties"/> </Server>Can't be much easier, and it does what it has to do.
15 comments:
This is great. What is the license of this code? I'd like to use it.
@Rafal
The license is "do whatever you want to do with it" ;-)
Great! Thanks, I created repo and uploaded artifact. It's on Apache License now (so - do whatever you want :-) ).
http://github.com/rafalrusin/log4j-initializer
We could add configurable time inverval.
I agree, this is a great approach. I already have a LifecycleListener to unload my DataSouce connection pool. Do you know if a log4j.properties dropped in the classpath will override this? I know tomcat will initialize log4j based upon the presence of a properties file in the classpath, but dont know if that will override if already configured?
Does the LogManager.shutdown() really kill the thread that configureAndWatch spawns (Daemon thread) ?
Any tips/tricks on how you would achieve startup/shutdown of log file configuration per webapp ?
cheers
Magnus
Thanks for the idea. I added the lifecycle in Tomcat, I can see that Log4j file is read, however the components still complain that Log4j is not configured correctly. Is there a classloading issue here? Has anyone else faced this issue?
Thanks for the idea. I added the lifecycle in Tomcat, I can see that Log4j file is read, however the components still complain that Log4j is not configured correctly. Is there a classloading issue here? Has anyone else faced this issue?
Regarding my previous comment, there was a error in my code. My log4j file is an XML so I should have been using DomConfigurator instead of PropertyConfigurator. Once I changed that the example worked like a charm. Thanks again.
P.S. Its strange though that no exception was thrown by log4j when I tried PropertyConfigurator with an xml file instead of properties file.
So... for XM users, change the next lines:
import org.apache.log4j.PropertyConfigurator;
becomes :
import org.apache.log4j.xml.DOMConfigurator;
and
PropertyConfigurator.configureAndWatch(propertiesFile);
becomes:
DOMConfigurator.configureAndWatch(propertiesFile);
So... for log4j.xml users: change the next lines:
import org.apache.log4j.PropertyConfigurator;
becomes:
import org.apache.log4j.xml.DOMConfigurator;
and
PropertyConfigurator.configureAndWatch(propertiesFile);
becomes:
DOMConfigurator.configureAndWatch(propertiesFile);
Great, I was able to do the same using ServletContextListener and keep the it per app instead..
@Override
public void contextInitialized(ServletContextEvent event) {
String path = event.getServletContext().getRealPath(propertiesFile);
PropertyConfigurator.configureAndWatch(path);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LogManager.shutdown();
}
propertiesFile could be context attribute
This seems like a good approach, but it doesn't work for me yet. When I have the log4j.properties in the classes folder, my loggers echo:
13/03/23 05:44:02 INFO com.myco.mypackage.myclass: *log message*
but when I load it via this method, my loggers echo:
13/03/23 05:44:02 INFO myclass: *log message*
The full path is missing. Also, if I change the log level to something differen, for examplet:
log4j.category.com.myco.mypackage.myclass=debug
it doesn't affect the log level that log4j outputs. Any suggestions?
The problem I was seeing was caused by a log4j jar in my web app's lib folder. I removed it (leaving the log4j jar in tomcat /lib) and it works.
Like Jan already said that he could have used AFTER_STOP_EVENT instead of the shutdownhook. I did that, as I was missing a lot of shutdown messages (guess the shutdownhook kicks in a little bit early).
We used it with tomcat 7, but it doesn't seem to be working with tomcat 8.5 anymore.
Post a Comment