使用高效的日誌工具—Log4J (2)

轉自:http://www.cnblogs.com/SGSoft/articles/84096.html

 

 Log4J的配置

現在來看log4j.properties配置文件的意義。第一行指定了根Logger的級別是DEBUG,並將此指定輸出到A1。A1 就是第二行定義的org.apache.log4j.ConsoleAppender,此行表示將A1輸出到控制檯。第三行規定了輸出到A1的格式爲 org.apache.log4j.PatternLayout。第四行規定了輸出到A1格式的轉換模式爲 org.javaresearch.log4j.TestLog4J。

很多成熟的服務器類的軟件日誌信息會輸出到控制檯,同時輸出到日誌文件備查。使用Log4J可以在不改變任何代碼的情況下,僅通過修改配置文件就可以輕鬆地完成這項功能。相關配置文件如下:

#### Use two appenders, one to log to console, another to log to a file
log4j.rootCategory=debug, stdout, R

# Print only messages of priority WARN or higher for your category
log4j.category.your.category.name=WARN

#### First appender writes to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

#### Second appender writes to a file
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

# Control the maximum log file size
log4j.appender.R.MaxFileSize=100KB
# Archive log files (one backup file here)
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n


這個配置文件指定了兩個輸出源stdout和R。前者把日誌信息輸出到控制檯,後者是一個輪轉日誌文件。最大的文件是100KB,當一個日誌文件 達到最大尺寸時,Log4J會自動把example.log重命名爲example.log.1,然後重建一個新的example.log文件,依次輪 轉。

在Web應用中使用

在Web應用中,應該在哪兒對Log4J進行配置呢?首先要明確,Log4J必須在應用的其它代碼執行前完成初始化。因爲Servlet 是在Web服務器啓動時立即裝入的,所以,在Web應用中一般使用一個專門的Servlet來完成Log4J的配置,並保證在web.xml的配置中,這 個Servlet位於其它Servlet之前。下面是一個例子,代碼如下:

package org.javaresearch.log4j;
import java.io.*;
import javax.servlet.*;
import org.apache.log4j.*;
public class Log4JInit extends HttpServlet {
 public void init() throws ServletException {
  String prefix = getServletContext().getRealPath("/");
  String file = getServletConfig().getInitParameter("log4j-config-file");
  // 從Servlet參數讀取log4j的配置文件 
  if (file != null) {
   PropertyConfigurator.configure(prefix + file);
  }
 }
 public void doGet(HttpServletRequest request,HttpServletResponse response)throws 
IOException, ServletException {}
 public void doPost(HttpServletRequest request,HttpServletResponse response)throws 
IOException, ServletException {}
}


<servlet>
  <servlet-name>log4jinit</servlet-name> 
   <servlet-class>org.javaresearch. log4j.Log4JInit</servlet-class>              
   <init-param>
   <param-name> log4j-config-file </param-name> 
      <param-value>/properties/log4j.properties</param-value> 
   </init-param>
   <load-on-startup>1</load-on-startup>
  </servlet>


注意:上面的load-on-startup應設爲1,以便在Web容器啓動時即裝入該Servlet。log4j.properties文件放在根的properties子目錄中,也可以把它放在其它目錄中。應該把.properties文件集中存放,這樣方便管理。

高級話題

性能

在記錄一些日誌信息時,會一定程度地影響系統的運行效率,這時日誌工具是否高效就是一個關鍵。Log4J的首要設計目標就是高效,一些關鍵組件都 重寫過很多次以不斷提高性能。根據Log4J項目小組的報告,在AMD Duron 800MHz + JDK1.3.1的環境下,Log4J判斷一條日誌語句是否需要輸出僅需要5納秒。實際的日誌語句執行的也非常快速,從使用SimpleLayout的 21微秒(幾乎與System.out.println一樣快),到使用TTCCLayout的37微秒不等。

嵌套診斷環境NDC

在多用戶併發的環境下,通常是由不同的線程分別處理不同的客戶端請求。此時要在日誌信息中區分出不同的客戶端,你可以爲每一個線程生成一個 Logger,從而從一堆日誌信息中區分出哪些信息是屬於哪個線程的,但這種方式並不高效。Log4J巧妙地使用了Neil Harrison提出的“NDC(嵌套診斷環境)”機制來解決這個問題。Log4J爲同一類別的線程生成一個Logger,多個線程共享使用,而它僅在日 志信息中添加能夠區分不同線程的信息。NDC是什麼?舉例來說,如果一個Servlet接到併發請求時,爲每一個客戶端創建一個新的線程,然後分配一個用 於保存該請求上下文的NDC堆棧。該上下文可能是發出請求的主機名、IP地址或其它任何可以用於標識該請求的信息。這樣,由於不同的客戶端處理線程具有不 同的NDC堆棧,即使這個Servlet同時生成多個線程處理不同的請求,這些日誌信息仍然可以區分出來,就好像Log4J爲每一個線程都單獨生成了一個 Logger實例一樣。在Log4J中是通過org.apache.log4j.NDC實現這種機制的。使用NDC的方法也很簡單,步驟如下:

1. 在進入一個環境時調用NDC.push(String),然後創建一個NDC;

2. 所做的日誌操作輸出中包括了NDC的信息;

3. 離開該環境時調用NDC.pop方法;

4. 當從一個線程中退出時調用NDC.remove方法,以便釋放資源。

下面是一個模擬記錄來自不同客戶端請求事件的例子,代碼如下:

import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
public class TestNDC {
 static Logger log = Logger.getLogger(TestNDC.class.getName());
 public static void main(String[] args) {
  log.info("Make sure %x is in your layout pattern!");
  // 從客戶端獲得IP地址的例子
  String[] ips = {"192.168.0.10","192.168.0.27"};
  for (int i = 0; i<ips.length ; i++)  // 模擬一個運行方法
  {
   // 將IP放進 NDC中
   NDC.push(ips[i]);
   log.info("A NEW client connected, who's ip should appear in this log message.");
   NDC.pop();
  }
  NDC.remove();
  log.info("Finished.");
 }
}


注意配置文件中的佈局格式中一定要加上%x。系統輸出如下:

INFO   - Make sure %x is in your layout pattern!
INFO  192.168.0.10 - A NEW client connected, who's ip should appear in this log 
message.
INFO  192.168.0.27 - A NEW client connected, who's ip should appear in this log 
message.
INFO   - Finished.


使用Log4J還是JDK logging API

從JDK 1.4.0開始,引入了java.util.logging包。雖然Log4J小組曾竭力遊說JCP(Java Community Process)採用Log4J作爲JDK 1.4的“標準”日誌API,雖然最終因Sun的日誌API規範的負責人Graham Hamilton的一句“Merlin的開發已經到了最後階段,這時不允許再對主要API做出改變”而沒有被採納,但Log4J還是對新的日誌API產生 了重要影響。那麼,我們到底應該採用Log4J還是java.util.logging包呢?下面僅對兩者做一簡單的比較。

1. Log4J更加成熟,從1999年10月開始至今已經有3年的時間,並且已經在許多項目中有着成熟的應用。而JDK中的logging包是在1.4之後才 引入的,並且不能運行於JDK 1.3之前的版本。Log4J則可以良好地運行於JDK 1.1之後的所有版本。

2. Log4J已經被移植到多種環境下,包括log4c(C)、log4cpp(C++)、log4perl(Perl)、log4net(.net)等。在這些環境下,可以感受到幾乎一致的配置和使用方式。這是JDK中的logging API所不能比擬的。

3. Log4J還具有更加強力的格式化系統,可以使記錄輸出時實現簡單的模式。但是,它不會增加類而導致格式化工具的擴展。衆多的附加程序和處理器使得Log4J數據包成爲一個絕佳的選擇,所有你所需要的都可能加以實現。

4. Log4J在性能上做了最大的優化。

Logging API對於簡單的使用是足夠的,但它缺少了許多Log4J所具有的功能。所以,如果你需要一個強力的logging機制,就應堅持使用Log4J;而如果只是需要一些簡單的控制或文件記錄,那麼可以使用已經內建在JDK之中的logging API。

雖然Log4J和JDK logging API是一種競爭關係,但在logging API還在JCP中討論(JSR47)時,兩者之間就已經開始相互影響了。

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