源碼賞析之spring對log4j的錦上添花

Log4j在系統記錄日誌方面功能很強大,但是,在配置文件路徑、日誌文件路徑及動態修改日誌記錄器級別等信息時卻不夠靈活,存在一些不足之處。好在spring提供了相關功能彌補了log4j在這方面的不足。接下來就來看一看spring是如何實現這方面功能

spring針對log4j提供瞭如下功能 
     1. 可以通過定期檢查配置文件的變化來動態的改變記錄級別和策略,不需要重啓Web應用。 web.xml配置如下:

<context-param>   

      <param-name>log4jRefreshInterval</param-name>   

      <param-value>6000</param-value>   

</context-param> 
     2. 日誌文件定在 web應用目錄下的任何位置,而不需要寫絕對路徑。 
因爲系統把web應用目錄的路徑壓入一個叫webapp.root的系統變量。這樣寫log文件路徑時不用寫絕對路徑了。當然也可以手動修改該系統變量。web.xml配置如下:

<context-param>

        <param-name>webAppRootKey</param-name>

        <param-value>muse.root</param-value>

</context-param>

注意:不同的應用不可以配置一樣的webAppRootKey值,否則會導致系統無法正常啓動。

log4j配置如下:log4j.appender.ROLLING_FILE.File=${muse.root}/WEB-INF/logs/muse.log 
   3. 可以把log4j.properties和其他properties一起放在/WEB-INF/ ,而不是Class-Path web.xml配置如下:

<context-param>

        <param-name>log4jConfigLocation</param-name>

        <param-value>/WEB-INF/log4j.xml</param-value>

</context-param>

注:如果要使webAppRootKey生效,則該項一定要配置,當然也可指定properties文件,因爲如果沒有配置的話,Log4jConfigListener是不會調用log4j初始化配置的。

接下來通過應用配置及源碼賞析兩方面來詳細瞭解一下這些功能。

應用配置

1.1 web.xml

    所有相關的web.xml配置信息如下:

<context-param>

    <param-name>webAppRootKey</param-name>

    <param-value>muse.root</param-value>

</context-param>

<context-param>

    <param-name>log4jConfigLocation</param-name>

    <param-value>/WEB-INF/log4j.xml</param-value>

</context-param>  

<context-param>   

      <param-name>log4jRefreshInterval</param-name>   

      <param-value>6000</param-value>   

  </context-param>   

  

<listener>   

   <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

</listener>  

1.2 log4j.properties配置

    所有相關的log4j.properties配置信息如下:

log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender

log4j.appender.ROLLING_FILE.Threshold=INFO

log4j.appender.ROLLING_FILE.File=${muse.root}/logs/muse.log

log4j.appender.ROLLING_FILE.Append=true

log4j.appender.ROLLING_FILE.MaxFileSize=5000KB

log4j.appender.ROLLING_FILE.MaxBackupIndex=100

log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout

log4j.appender.ROLLING_FILE.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n 

源碼賞析

2.1 Log4jConfigListener

    這是應用啓動時即調用的監聽類。該類不具體處理業務邏輯,直接調用Log4jWebConfigurer.initLogging()

public class Log4jConfigListener implements ServletContextListener {

public void contextInitialized(ServletContextEvent event) {

Log4jWebConfigurer.initLogging(event.getServletContext());

}

}

2.2 Log4jWebConfigurer

    這是一個便於在web環境定製log4j初始化的類。即log4j初始化前需要做的一些事情的處理類,當然它也包括調用處理具體初始化工作的類。

public abstract class Log4jWebConfigurer {

/** 該屬性用來指定log4j 配置文件的路徑。 */

public static final String CONFIG_LOCATION_PARAM = "log4jConfigLocation";

/** 該屬性用來指定檢查log4j 配置文件的間隔時間。 */

public static final String REFRESH_INTERVAL_PARAM = "log4jRefreshInterval";

/** 該屬性用來指定是否設置web應用根目錄環境變量。 */

public static final String EXPOSE_WEB_APP_ROOT_PARAM = "log4jExposeWebAppRoot";

/**

 * 初始化log4j,包括設置web應用根目錄系統屬性。

 */

public static void initLogging(ServletContext servletContext) {

if (exposeWebAppRoot(servletContext)) {

WebUtils.setWebAppRootSystemProperty(servletContext);

}

String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);

if (location != null) {

try {

if (!ResourceUtils.isUrl(location)) {

// 解決系統屬性佔位符的問題。

location = SystemPropertyUtils.resolvePlaceholders(location);

location = WebUtils.getRealPath(servletContext, location);

}

servletContext.log("Initializing log4j from [" + location + "]");

String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);

if (intervalString != null) {

try {

long refreshInterval = Long.parseLong(intervalString);

Log4jConfigurer.initLogging(location, refreshInterval);

}

catch (NumberFormatException ex) {

throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + ex.getMessage());

}

}

else {

Log4jConfigurer.initLogging(location);

}

}

catch (FileNotFoundException ex) {

throw new IllegalArgumentException("Invalid 'log4jConfigLocation' parameter: " + ex.getMessage());

}

}

}

/**

 * 返回是否進行web應用根目錄系統屬性設置。

 */

private static boolean exposeWebAppRoot(ServletContext servletContext) {

String exposeWebAppRootParam = servletContext.getInitParameter(EXPOSE_WEB_APP_ROOT_PARAM);

return (exposeWebAppRootParam == null || Boolean.valueOf(exposeWebAppRootParam));

}

}

2.3 WebUtils

    這個類是web應用程序的五花八門的工具箱。比如設置web應用目錄系統屬性等。

public abstract class WebUtils {

public static final String WEB_APP_ROOT_KEY_PARAM = "webAppRootKey";

/** Default web app root key: "webapp.root" */

public static final String DEFAULT_WEB_APP_ROOT_KEY = "webapp.root";

/**

 * 添加一個系統屬性用於保存web應用根目錄信息。系統屬性鍵可以在web.xml文件的context-param節點定義。默認值爲webapp.root

 * 如果不同的應用使用相同的該系統屬性鍵則會報錯。

 */

public static void setWebAppRootSystemProperty(ServletContext servletContext) throws IllegalStateException {

Assert.notNull(servletContext, "ServletContext must not be null");

String root = servletContext.getRealPath("/");

if (root == null) {

throw new IllegalStateException(

    "Cannot set web app root system property when WAR file is not expanded");

}

String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);

String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);

String oldValue = System.getProperty(key);

if (oldValue != null && !StringUtils.pathEquals(oldValue, root)) {

throw new IllegalStateException(

    "Web app root system property already set to different value: '" +

    key + "' = [" + oldValue + "] instead of [" + root + "] - " +

    "Choose unique values for the 'webAppRootKey' context-param in your web.xml files!");

}

System.setProperty(key, root);

servletContext.log("Set web app root system property: '" + key + "' = [" + root + "]");

}

 

/**

 * 根據相對路徑得到web應用下的絕對路徑

 */

public static String getRealPath(ServletContext servletContext, String path) throws FileNotFoundException {

Assert.notNull(servletContext, "ServletContext must not be null");

if (!path.startsWith("/")) {

path = "/" + path;

}

String realPath = servletContext.getRealPath(path);

if (realPath == null) {

throw new FileNotFoundException(

"ServletContext resource [" + path + "] cannot be resolved to absolute file path - " +

"web application archive not expanded?");

}

return realPath;

}

}

2.4 Log4jConfigurer

    這是一個具體實現log4j配置的類。該類直接調用log4j組件的相關類進行日誌記錄器的配置。

public abstract class Log4jConfigurer {

public static final String CLASSPATH_URL_PREFIX = "classpath:";

public static final String XML_FILE_EXTENSION = ".xml";

/**

 * 從指定的配置文件路徑來初始化log4j,該方法不支持定時檢查配置文件變更功能。

 */

public static void initLogging(String location) throws FileNotFoundException {

String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);

URL url = ResourceUtils.getURL(resolvedLocation);

if (resolvedLocation.toLowerCase().endsWith(XML_FILE_EXTENSION)) {

DOMConfigurator.configure(url);

}

else {

PropertyConfigurator.configure(url);

}

}

/**

 * 從指定的配置文件路徑來初始化log4j,該方法支持定時檢查配置文件變更功能。

 */

public static void initLogging(String location, long refreshInterval) throws FileNotFoundException {

String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);

File file = ResourceUtils.getFile(resolvedLocation);

if (!file.exists()) {

throw new FileNotFoundException("Log4j config file [" + resolvedLocation + "] not found");

}

if (resolvedLocation.toLowerCase().endsWith(XML_FILE_EXTENSION)) {

DOMConfigurator.configureAndWatch(file.getAbsolutePath(), refreshInterval);

}

else {

PropertyConfigurator.configureAndWatch(file.getAbsolutePath(), refreshInterval);

}

}

}

2.5 ResourceUtils

   這是一個用來解決文件資源定位的工具類。

public abstract class ResourceUtils {

public static final String CLASSPATH_URL_PREFIX = "classpath:";

/**

 * 返回指定的資源定位字符串是否是一個URL

 */

public static boolean isUrl(String resourceLocation) {

if (resourceLocation == null) {

return false;

}

if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {

return true;

}

try {

new URL(resourceLocation);

return true;

}

catch (MalformedURLException ex) {

return false;

}

}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章