許多應用, 特別是企業應用, 都需要日志記錄程序。應用日志有助于服務工程師解決這個領域的難題, 并且能為安全分析提供審計跟蹤。
最初, Java 平臺包含了很少可用于應用日志記錄的資源。被迫面對日志文件中的println語句的開發人員和系統管理員,有的設法發明自家的解決方案,有的轉而采用由商業開發商或者開放源碼開發者生產的眾多優秀(但互不兼容)的日志記錄產品中的一種。Java 2版本1.4 改變了這種局面,因為它引入了java.util.logging包。
本期技巧簡要地解釋了Java日志記錄的基本知識,并提供了示例代碼,演示如何通過Java消息服務 (JMS)發送應用日志消息到某個獨立客戶端端。如果您不熟悉JMS, 參看2003年4月15 日那一期的“Publish/Subscribe Messaging With JMS Topics”以及 2003年3月11 日那一期的“使用JMS Queues(Using JMS Queues)”。
java.util.logging 包
Java logging包最初是作為Java Specification Request (JSR-047)創建的,現在是Java 2, Standard Edition (J2SE)的一部份。它的需求包括以下特性:
- 最小的運行時性能影響。
- 運行時啟用/禁用日志記錄。
- 細粒度控制。
- 運行時日志記錄服務注冊。
- 與現有日志記錄服務的互操作性, 比如,系統日志和遺留日志記錄方案。
- 在合適的時候向用戶顯示高優先級消息。
- 能夠處理國際化的日志記錄消息。
- 能夠記錄對象, 而不只是記錄字符串 。
由于logging包是J2SE 的一部分, 它在其他Java 平臺也是可用的, 包括J2EE。具體地,Java logging可以用于應用客戶端、servlets 、JSP 頁面、企業bean 和連接器。
Logging類和接口
日志記錄程序的主要類是 Logger。 Logger表示一種通道,記錄(logging)消息可以通過它進行發送。通常,每個類有它自己的Logger。
Logger 用Level來配置,這是一個指明問題嚴重性的類,用于指明所報告的問題的嚴重性和/或該類需要的詳情級別。每一條發送到Logger的消息都有一個關聯 Level。 Logger 只報告那些Level比該Logger高或者與之相同的錯誤。
最高的日志記錄 Level 是 SEVERE, 表明碰到了一個嚴重問題—— 經常指的是一個致命錯誤。其他 Level 值(以遞減順序) 是 WARNING 和 INFO, 分別為可恢復的錯誤和信息類消息。 CONFIG 級別表明, 配置事件(比如正在讀一個屬性文件) 發生過。編程人員可以使用 的Level 值有: FINE、FINER 和 FINEST ,以連續報告更加細粒度的登錄消息。服務工程師可以使用這些 Level值來隱藏外部細節, 或在類到類(class-by-class)的基礎上增加日志記錄細節的級別。Logger的缺省 Level 是 INFO。
LogRecord 是一個表示應該寫入日志的一條消息的對象。它包含各種各樣的信息, 包括被打印的消息, 最初發送給Logger的名字, Level 和所發送消息的創建時間與日期, 以及調用者的線程id。
一個叫做 Handler的抽象類表示一個知道如何以一種有用方式表示 LogRecord的類。logging包包含一些 Handler類 (比如 ConsoleHandler、FileHandler、StreamHandler 和 SocketHandler) ,它們報告 LogRecords 各種類型的目標。每一 Logger都可以有多 個Handler。 每個 Handler 還可以有一個 Level, 摒除任何 LogRecord ,其Level 在它自己之下。因此, 例如, 單個 Logger 可能有二個Handler: 一個用于在磁盤上寫入日記文件,而另一個用于將日志消息發送到系統管理員的尋呼機。磁盤日志 Handler 可以用Level FINEST來配置,因此它將所有日志消息寫入磁盤。相反, 尋呼機日志 Handler可能只報告 SEVERE 日志消息。
Formatter類(及其所定義的任何子類)將LogRecord轉換為String以便打印。例如, Formatter 的XMLFormatter 子類將LogRecord 寫為格式良好的XML。開發人員可以編寫定制的 Formatter 類。
最后, Filter 接口允許編程人員編寫方法(isLoggable),該方法允許進行規劃性的控制,在這種控制下丟棄或者打印LogMessages。
示例代碼
本技巧的示例代碼示范了如何設置 Logger, 并使用它發送XML 格式的日志消息,從Web層,通過JMS,到達一個獨立JMS 客戶。
本技巧的示例代碼包含一個servlet 和一個獨立客戶端端。servlet 稱作 LogDemoServlet。獨立客戶端端稱作LoggingReceiver。獨立客戶端端監聽正發布給JMS主題的消息, 并打印接收到任何TextMessages。示例應用包含一個用戶向LogDemoServlet張貼數據的HTML表單。

注意, 表單提示用戶下載一個JAR文件,然后運行該文件作為一個獨立客戶端。在通過表單張貼數據之前,用戶需要完成這些步驟。
servlet 通過向Topic發布一條XML 格式的消息來作出響應。如果 LoggingReceiver正在運行, 用戶會看到在屏幕上打印出一條XML 格式的消息。
例如:
<?xml version="1.0" encoding="MacRoman" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2003-05-05T23:51:20</date>
<millis>1052200280257</millis>
<sequence>0</sequence>
<logger>com.elucify.tips.may2003.LogDemoServlet</logger>
<level>INFO</level>
<class>com.elucify.tips.may2003.LogDemoServlet</class>
<method>doPost</method>
<thread>10</thread>
<message>quest= { 'Johnny' }
FavoriteColor= { 'Violet' }
name= { 'Johnny' }
</message>
</record>
示例代碼中的關鍵部分是servlet LogDemoServlet。這個servlet 簡單地將它的POST參數格式化為一個字符串, 并在servlet每次被調用時將該字符記錄到Logger 中。方法doPost的開始處設置了 Logger:
// Handle post request
public void doPost(HttpServletRequest req,
HttpServletResponse res)
throws IOException, ServletException {
res.setContentType("text/html");
PrintWriter pw = res.getWriter();
// Get POST parameters and write them as
// "variable=value" to a ByteArrayOutputStream
String postParams = getPostParams(req);
ByteArrayOutputStream bos =
new ByteArrayOutputStream();
Logger logger = Logger.getLogger(
this.getClass().getName());
servlet 設置內容類型、獲得一個Writer, 并格式化POST參量。然后它通過調用靜態方法 Logger.getLogger(this.getClass().getName())來創建了一個Logger。這個方法返回該名稱現有的Logger, 或者如果不存在的話,就創建一個。代碼使用了servlet 類的完全限定名。這是一個公共約定,以確保系統中各種Logger的名稱不會發生沖突。
缺省情況下,Logger將它所創建的任何消息寫入到標準輸出,而不管運行什么代碼。例如,Web層消息通常寫入Web服務器日志文件。
doPost方法的第二部分向Logger添加一個新的 Handler 。每次消息記錄到Logger時, Logger發送消息到兩個 Handlers (假設消息的日志級別足夠高) 。下面是的doPost方法的第二部分 :
// Format logging messages as XML,
// store in byte array
StreamHandler sh = new StreamHandler(
bos, new XMLFormatter());
logger.addHandler(sh);
logger.log(Level.INFO, postParams);
sh.flush();
// Send contents of buffer as JMS message
// to listeners
publish(bos.toString());
pw.println(
"Logging messages sent to subscribers");
}
StreamHandler 是一個使用Formatter 來將LogRecord 格式化為字符串,并將它寫入OutputStream的處理程序。在本例中, OutputStream 是 ByteArrayOutputStream, 因此日志記錄結果被寫入內存。這個方法還使用一個 XMLFormatter,以便 LogRecords 被寫作XML文件 。對addHandler的調用將新的StreamHandler添加到Logger,然后將包含格式化POST參數的日志消息發送到 Logger。Logger 發送消息到它的所有Handlers,包括新的StreamHandler。對flush()的調用確保了所有已寫入字節被刷新到ByteArrayOutputStream。最后, ByteArrayOutputStream 被轉換成XML字符串。再通過發布方法將字符串發布給一個JMS Topic。關于如何將消息發送到JMS Topic的詳細描述(使用同樣的發布方法) ,請參看2003年4月15 日那一期的技巧“Publish/Subscribe Messaging With JMS Topics”。
posted on 2005-02-04 11:25
jacky 閱讀(349)
評論(0) 編輯 收藏