在Tomcat 定製JNDI URL Resource中我介紹瞭如何在tomcat中實現URL資源訪問,在Log4j配置實踐中我介紹了在Web應用中如何使用log4j xml配置方式記錄日誌。這篇我要介紹如何在Web應用中把Log4j和JNDI結合起來,達到可以動態修改Log4j配置文件路徑和Log4j日誌文件存儲路徑的目的。
我們知道,Spring本身提供了Log4jConfigListener和Log4jWebConfigurer來幫助初始化Log4j,其中參數log4jConfigLocation可以在classpath或者按指定路徑查找log4j配置文件,log4jRefreshInterval指定檢查加載配置文件變化。在這裏,我們要做的是根據Log4jConfigListener和Log4jWebConfiguer來定製自己的Log4jConfigListener,達到可以按JNDI來訪問配置文件的目的。給出實現如下:
package com.demo;
/*...
import ...here
...
*/
public class JndiLog4jConfigListener implements ServletContextListener {
/** Parameter specifying the location of the log4j config file */
public static final String CONFIG_LOCATION_PARAM = "log4jConfigLocation";
/** Parameter specifying the location of the log4j config file */
public static final String CONFIG_FILENAME_PARAM = "log4jConfigFilename";
/** Parameter specifying the refresh interval for checking the log4j config file */
public static final String REFRESH_INTERVAL_PARAM = "log4jRefreshInterval";
/** Parameter specifying whether to expose the web app root system property */
public static final String EXPOSE_WEB_APP_ROOT_PARAM = "log4jExposeWebAppRoot";
public void contextInitialized(ServletContextEvent event) {
// Expose the web app root system property.
if (exposeWebAppRoot(servletContext)) {
WebUtils.setWebAppRootSystemProperty(servletContext);
}
// Only perform custom log4j initialization in case of a config file.
String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
String filename = servletContext.getInitParameter(CONFIG_FILENAME_PARAM);
if (location != null) {
// Perform actual log4j initialization; else rely on log4j's default initialization.
try {
Context ic = new InitialContext();
location = ((URL)ic.lookup(location)).toExternalForm();
location = location + File.separator + filename;
// Write log message to server log.
servletContext.log("Initializing log4j from [" + location + "]");
// Check whether refresh interval was specified.
String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);
if (intervalString != null) {
// Initialize with refresh interval, i.e. with log4j's watchdog thread,
// checking the file in the background.
try {
long refreshInterval = Long.parseLong(intervalString);
Log4jConfigurer.initLogging(location, refreshInterval);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + ex.getMessage());
}
}
else {
// Initialize without refresh check, i.e. without log4j's watchdog thread.
Log4jConfigurer.initLogging(location);
}
}
catch (Exception ex) {
throw new IllegalArgumentException("Invalid log4j Location: " + ex.getMessage());
}
}
}
public void contextDestroyed(ServletContextEvent event) {
servletContext.log("Shutting down log4j");
try {
Log4jConfigurer.shutdownLogging();
}
finally {
// Remove the web app root system property.
if (exposeWebAppRoot(servletContext)) {
WebUtils.removeWebAppRootSystemProperty(servletContext);
}
}
}
private static boolean exposeWebAppRoot(ServletContext servletContext) {
String exposeWebAppRootParam = servletContext.getInitParameter(EXPOSE_WEB_APP_ROOT_PARAM);
return (exposeWebAppRootParam == null || Boolean.valueOf(exposeWebAppRootParam).booleanValue());
}
}
Web.xml的配置如下:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>jndi:java:/comp/env/config_dir</param-value>
</context-param>
<context-param>
<param-name>log4jConfigFilename</param-name>
<param-value>log4j-demo.xml</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>6000</param-value>
</context-param>
<listener>
<listener-class>com.demo.JndiLog4jConfigListener</listener-class>
</listener>
<resource-ref>
<res-ref-name> config_dir </res-ref-name>
<res-type> java.net.URL </res-type>
<res-auth> Container </res-auth>
</resource-ref>