Some of you may be familiar with following log statement in your console or log file.
log4j:WARN No appenders could be found for logger (com.foo.xxx). log4j:WARN Please initialize the log4j system properly.
This happens when your application or a framework like spring, hibernate, etc. tries to make a log statement and log4j is not yet initialized. The point is that you must configure your log4j before your application or framework tries to log something.
Old School Servlet Approach
In a web environment you could use a servlet with load-on-startup which specifies that the servlet should be loaded when the web application is started. In that servlet you could initialize log4j, but that’s the old-fashioned way
<servlet>
<servlet-name>log4j-init</servlet-name>
<servlet-class>com.foo.Log4jInit</servlet-class>
<init-param>
<param-name>log4j-init-file</param-name>
<param-value>WEB-INF/classes/log4j.lcf</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
See http://logging.apache.org/log4j/1.2/manual.html for more Details.
EJB 3.1 Approach
With Java EE 6, especially EJB 3.1 we have the possibility to write an EJB 3.1 Application Startup Bean which can do the initialization of log4j for use.
@Singleton
@Startup
public class Log4jStartupBean{
@PostConstruct
private void startup() { ... }
@PreDestroy
private void shutdown() { ... }
...
}
What we need is our log4j.xml configuration file. I choose the approach to put the configuration file it in the META-INF directory and get the file path with Class.getResource(String name). An alternative could be to use System.getProperty(String key) to get the file path to a system wide log4j configuration file set by your application server or by resource injection @Resource(name=”…”) from the EJB container.
For the META-INF approach a typical mavenized project with a resources folder is all we need.
my-app
|-- pom.xml
`-- src
|-- main
| |-- java
| | `-- com
| | `-- mycompany
| | `-- app
| | `-- Log4jStartupBean.java
| `-- resources
| `-- META-INF
| `-- log4j.xml
`-- test
Note: When your EJB 3.1 project is a WAR module and you use the META-INF approach you have to add the resources directory as webResources by the maven-war-plugin. See http://maven.apache.org/plugins/maven-war-plugin/
So now we just need to initialize log4J in the startup() Method in the Log4jStartupBean class.
@Singleton
@Startup
public class Log4jStartupBean{
private static final String LOG4J_XML = "/META-INF/log4j.xml";
private Logger logger = null;
...
@PostConstruct
public void startup() {
URL url = this.getClass().getResource(LOG4J_XML);
if(url == null || url.getFile().length() == 0) {
throw new RuntimeException("Log4j config file "+ LOG4J_XML +" not found");
}
DOMConfigurator.configure(url.getFile());
logger = Logger.getLogger(this.getClass());
logger.info("logger initialized with "+ LOG4J_XML + "...");
...
}
...
}
In this example code we used the DOMConfigurator class to configure log4j with a XML file. More information about the XML configuration can be found on the log4j wiki page.
You could use a log4j properties file for the configuration but then you have to use the PropertyConfigurator class.
When you have other singelton startup beans and these beans also need to use log4j, we must determine the starting order of the beans with the @DependsOn Annotation.
For further Reading about EJB 3.1 I would recommend you the following sites:
- http://blogs.sun.com/enterprisetechtips/entry/a_sampling_of_ejb_3
- http://openejb.apache.org/singleton-ejb.html
Note: How to configure multiple log4j for different war modules in a single EAR see http://stackoverflow.com/questions/762918/how-to-configure-multiple-log4j-for-different-wars-in-a-single-ear
Note: I tested this example with GlassFish 3.0.1 as WAR module.