JDK1.4的java.util.Logging包的使用說明與示例
Sun公司推出的JDK1.4版本在java.util.Logging軟體包中新增加了處理應用程式日誌工作的核心API函式。這個Java日誌軟體包提供了一種為Java應用程式嵌入多級日誌的簡單而又靈活的方法。
簡要介紹
java.util.Logging包括1個介面: Filter;15個類: Logger, LogManager, ErrorManager, Level, LogRecord, LoggingPermission, Handler, MemoryHandler, StreamHandler, ConsoleHandler, FileHandler, SocketHandler, Formatter, SimpleFormatter, XMLFormatter,
處理器的繼承關係
Handler |
MemoryHandler |
ConsoleHandler |
StreamHandler |
FileHandler |
SocketHandler |
格式化器的繼承關係
Formatter |
SimpleFormatter |
XMLFormatter |
類/介面功能概要
1.Filter(過濾器):控制日誌記錄的輸出粒度, 粒度範圍超過了Level類提供的功能,在每個handler的setFilter方法中設定該型別
2.Logger(日誌記錄器): 為了利用日誌軟體包,Logger物件並呼叫Logger中多個記錄方法中的某一個,例如info(String message)。日誌記錄器Logger建立一個LogRecord(包含應用程式具體的日誌資訊)物件並把它傳遞到一個或者多個Handler,Handler物件再把它輸出到目標,這樣就實現了日誌記錄的輸出。在日誌的輸出過程中,Logger和Handler物件可以根據日誌等級和過濾器(Filter)來判斷那些日誌需要記錄下來。同時,Handler與Formatter協同工作,Formatter對日誌資訊進行格式化,決定日誌記錄如何輸出到日誌
3.LogManager(日誌管理器):管理一個日誌記錄器物件的名稱空間,所有已命名的日誌記錄器都被儲存在這個名稱空間中;管理一套日誌記錄的控制屬性,這些是簡單的值對像,可以被Handler和logging物件使用來進行自我配置;
4.ErrorManager(錯誤管理器): ErrorManager物件依附在Handler物件上,用來處理在記錄日誌期間發生在Handler物件上的任何錯誤.但在處理輸出日誌時,假如Handler物件遇到問題時, Handler並沒有丟擲異常給日誌記錄的呼叫者,而是用依附在Handler上的ErrorManager來處理。
5.Level(等級): Java 日誌軟體包對每一個日誌訊息都賦以一個等級,以控制日誌記錄的輸出。等級是一個整型資料,記錄的等級越高,那麼這個整型資料也就越大。下面的等級是在Level類中定義的(按照從低到高的順序排列):
級別 |
重要性 |
日誌記錄器相應的日誌方法 |
級別數值 |
SEVERE |
非常重要 |
severe(String message); |
1000 |
WARNING |
針對警告 |
warning(String message); |
900 |
INFO |
資訊化的執行時間訊息 |
info(String message); |
800 |
CONFIG |
靜態設定的訊息 |
config(String message); |
700 |
FINE |
提供追蹤資訊 |
fine(String message); |
500 |
FINER |
顯示詳細的追蹤資訊 |
finer(String message); |
400 |
FINEST |
更詳細的追蹤資訊 |
finest(String message); |
300 |
ALL |
顯示所有應該日誌記錄的資訊 |
沒有可應用的 |
Integer.MIN_VALUE |
OFF |
關閉日誌 |
沒有可應用的 |
Integer.MAX_VALUE |
6.LogRecord(記錄資訊): LogRecord物件用來在日誌框架和單個日誌記錄Handler之間傳遞日誌記錄請求,當一個LogRecord被傳遞進框架時, 在邏輯上它是屬於框架的,不應該被客戶應用程式使用或更新
注意:假如客戶應用程式沒有指定一個顯式的來源方法名稱和來源類名稱, 當他們(方法或類)第一次被訪問時(由於對getSourceMethodName或者getSourceClassName方法的呼叫),LogRecord類將通過分析呼叫堆疊,自動推斷他們.因此,假如一個日誌處理器想停止傳遞一個LogRecord給另一個執行緒,或者通過RMI傳播,並且假如它後來想得到方法和類名,它應該呼叫getSourceClassName或getSourceMethodName來確保有值被填入.
7.LoggingPermission(記錄許可):當代碼和一個呼叫日誌記錄控制方法的安全管理器(SecurityManager)一起執行的時候,安全管理器(SecurityManager)將檢測這個許可.當前僅僅有一個LoggingPermission.這值控制,並且它有控制日誌記錄配置的能力,例如新增或刪除Handler,新增或刪除過濾器,或者改變日誌等級.程式設計師不必直接建立LoggingPermission物件.而是由讀取的安全策略檔案(security policy file)來建立
8.Handler(處理器): Handler物件從Logger那裡獲得日誌資訊並且將它輸出。可能會輸出到控制檯,或者輸出到檔案,或者把這些日誌資訊傳送到網路中的日誌服務,或者把它們轉發給作業系統日誌,或者其他什麼。。JDK1.4日誌API提供了兩大類Handler物件。MemoryHandler簡單的把日誌訊息儲存在迴圈記憶體緩衝(circular memory buffer),當特定的觸發事件發生時,再把它們釋出到目標handler。典型的觸發事件是接收到等級符合釋出等級的日誌訊息。如果需要其它釋出標準,MemoryHandler類可以擴充套件,重寫日誌方法來按照使用者定義的日誌記錄條件來發布記憶體緩衝。StreamHandler向指定的輸出流釋出日誌記錄。Java日誌API指定了三種StreamHandler物件。ConsoleHandler向標準錯誤流釋出日誌記錄。FileHandler向指定的檔案釋出日誌記錄,它也可以配置成向迴圈檔案集寫入日誌記錄。SocketHandler向網路流釋出日誌記錄(與其它應用通訊)。可以通過呼叫setLevel(Level.OFF)使處理器無效,也可以通過呼叫setLevel方法的一個適當的等級使其有效.處理器類用LogManager屬性來設定處理器的過濾器,格式化和等級的預設值.
9.MemoryHandler(儲存處理器):把請求緩衝在記憶體的迴圈緩衝區內,通常這個處理器簡單地把引入的LogRecord存貯在緩衝區,並丟棄以前的記錄.這個緩衝很便宜並且避免了格式化.在某些特殊情況下, MemoryHandler將把當前緩衝區中內容擠出到目標緩衝區. 主要有三種引發擠壓緩衝區的模型:
n引入的LogRecord有一個大於以前定義等級的型別,也就是擠壓等級
n一個外部類顯示地呼叫擠壓方法
n一個子類過載log方法,並且掃描每個引入的LogRecord,假如它匹配需求標準,將呼叫擠壓方法
10.StreamHandler(流處理器):流基於日誌記錄處理器(logging Handler),這主要是作為一個基類或支援類來實現其他日誌記錄處理器. LogRecord被髮送到一個給定的java.io.OutputStream.
11.ConsoleHandler(控制檯處理器):這個處理器向標準錯誤流(System.err)傳送日誌記錄,預設SimpleFormatter被用來產生簡短的概要
12.FileHandler(檔案處理器):簡單檔案日誌處理器.FileHandler可以寫到一個指定的檔案,或者一套迴圈檔案.對一套迴圈檔案,當每個檔案達到一個指定大小的時候,它將被關閉,迴圈出去,一個新的檔案被開啟.老的檔案將按順序加上"0", "1", "2". 預設用XMLFormatter來格式化
13.SocketHandler(網路處理器):釋出LogRecord到一個網路流連線.預設用XMLFormatter來格式化
14.Formatter(格式化器):格式化器提供格式化LogRecord支援.每個日誌處理器器都有一個格式化器格.式化器獲得一個LogRecord,並把它轉化為字串.有一些格式化器(例如XMLFormatter)需要包裝頭和尾字串圍繞一套格式化記錄. GetHeader和getTail方法可以用來獲得這些字串.
15.SimpleFormatter(簡單格式化器): Print a brief summary of the LogRecord in a human readable format. The summary will typically be 1 or 2 lines用一種可以閱讀的格式來列印LogRecord的簡單概要.典型的概要是1或者2行.
16.XMLFormatter(XML格式化器):把LogRecord格式化尾標準的XML格式.DTD規範在Java Logging APIs規範的附錄A中提供
示例剖析
1.簡單示例
這個示例說明logging的常用操作
public class JDK14LoggingTest {
/**
日誌記錄器
*/
Logger log = Logger.getLogger(getClass().getName());
/**
* 日誌輸出物件
*/
Handler fileHandler = null;
/**
* 構造方法
*/
public JDK14LoggingTest() {
//設定輸出到檔案
try {
fileHandler = new FileHandler("%h/java%u.log");
//fileHandler = new FileHandler("%h/java%u.log",10,5,true);
//設定記錄級別
fileHandler.setLevel(Level.WARNING);
//設定過濾器,已達到更細粒度的記錄
//fileHandler.setFilter();
//設定輸出格式,有兩種型別:SimpleFormatter和XMLFormatter,
//預設是XMLFormatter,當然自己也可以擴張,只要繼承Formatter類就可以了
fileHandler.setFormatter(new SimpleFormatter());
//指定日誌編碼格式
//fileHandler.setEncoding("");
//設定錯誤處理
//fileHandler.setErrorManager(new ErrorManager());
}
catch (Exception ex) {
ex.printStackTrace();
}
log.addHandler(fileHandler);
}
public void testLog(){
log.info(getClass().getName());
log.log(Level.WARNING,getClass().getName(),"testLog");
}
public static void main(String[] args) {
JDK14LoggingTest test = new JDK14LoggingTest();
test.testLog();
}
}
2.資料庫Handler示例
從上面的描述可以看出,jdk1.4的logging沒有提供輸出到資料庫的Handler,下面將介紹一個怎樣把日誌輸出到資料庫, 我們知道Handler物件負責從Logger那裡獲取日誌資訊然後把它輸出到目的地。而使用者希望把日誌儲存到資料庫中,其實這只是把日誌資訊的輸出目標替換為資料庫而已。所以編寫擴充套件Handler的JDBCHandler類,由它來實現把日誌資訊寫入到資料庫中。要想擴充套件Handler類,必須實現Handler定義的三個抽象函式(abstract):
public abstract void flush() ;
public abstract void publish(LogRecord record) ;
public abstract void close() throws java.lang.SecurityException;
其中,publish(LogRecord record)方法最為重要,它最終實現把日誌資訊輸出到目標。close()方法負責釋放所有handler所擁有的資源。這次我們只在publish(LogRecord record)方法中實現把日誌資訊(封裝在LogRecord物件中)通過合適的JDBC驅動程式插入到資料庫中就可以了, 下面我就以向MySql資料庫輸出日誌記錄為例,完整原始碼的實現如下:
package com.benqguru.commons.logging.test;
import java.util.logging.*;
import java.sql.*;
class JDBCHandler
extends Handler {
public void flush() {
}
public void publish(LogRecord record) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
Class.forName("org.gjt.mm.mysql.Driver");
conn = DriverManager.getConnection("mysql://10.89.58.228:3306/logtest","root","");
if (conn != null) {
Object[] content = record.getParameters();
String sql = "insert into MyLog(logTime,EventType) values(?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, record.getMillis());
pstmt.setString(2, (String) content[0]);
//save loginfo in databse
pstmt.execute();
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (conn != null) {
try {
conn.close();
}
catch (Exception ex) {