Log4J Java日志框架特性的內(nèi)部實現(xiàn)
由于 Log4J 得到廣泛應(yīng)用,從使用者的角度考慮,本文所設(shè)計的框架,采用了部分 Log4J 的接口和概念,但內(nèi)部實現(xiàn)則完全不同。使用 Java 實現(xiàn)日志框架,關(guān)鍵的技術(shù)在于前面提及的Java日志框架特性的內(nèi)部實現(xiàn),特別是:日志的分類和級別、日志分發(fā)框架的設(shè)計、日志記錄器的設(shè)計以及在設(shè)計中的高性能和高穩(wěn)定性的考慮。
2.1系統(tǒng)架構(gòu)
Java日志框架可以分為日志記錄模塊和日志輸出模塊兩大部分。日志記錄模塊負(fù)責(zé)創(chuàng)建和管理日志記錄器 (Logger) ,每一個 Logger 對象負(fù)責(zé)按照不同的級別 (LoggerLevel) 接收各種記錄了日志信息的日志對象 (LogItem) , Logger 對象首先獲取所有需要記錄的日志,并且同步地將日志分派給日志輸出模塊。日志輸出模塊則負(fù)責(zé)日志 輸出器 (Appender) 的創(chuàng)建和管理,以及日志的輸出。系統(tǒng)中允許有多個不同的日志 輸出器 ,日志 輸出器 負(fù)責(zé)將日志記錄到存儲介質(zhì)當(dāng)中。系統(tǒng)結(jié)構(gòu)如下圖 1 所示:
圖 1 ,Java日志系統(tǒng)結(jié)構(gòu)圖
下圖 2 使用 UML 類圖給出了Java日志系統(tǒng)的架構(gòu):
圖 2 ,日志系統(tǒng)框架架構(gòu)圖
在圖 2 給出的架構(gòu)中,日志記錄器 Logger 是整個日志系統(tǒng)框架的用戶使用接口,程序員可以通過該接口記錄日志,為了實現(xiàn)對日志進(jìn)行分類,系統(tǒng)設(shè)計允許存在多個 Logger 對象,每一個 Logger 負(fù)責(zé)一類日志的記錄, Logger 類同時實現(xiàn)了對其對象本身的管理。 LoggerLevel 類定義了整個日志系統(tǒng)的級別,在客戶端創(chuàng)建和發(fā)送日志時,這些級別會被使用到。 Logger 對象在接收到客戶端創(chuàng)建和發(fā)送的日志消息時,同時將該日志消息包裝成日志系統(tǒng)內(nèi)部所使用的日志對象 LogItem ,日志對象除了發(fā)送端所發(fā)送的消息以外,還會包裝諸如發(fā)送端類名、發(fā)送事件、發(fā)送方法名、發(fā)送行號等等。這些額外的消息對于系統(tǒng)的跟蹤和調(diào)試都非常有價值。包裝好的 LogItem 最終被發(fā)送給 輸出器 ,由這些 輸出 器負(fù)責(zé)將日志信息寫入最終媒介, 輸出器 的類型和個數(shù)均不固定,所有的 輸出器 通過 AppenderManager 進(jìn)行管理,通常通過配置文件即可方便擴展出多個 輸出器 。
2.2日志記錄部分的設(shè)計
如前文所述,日志記錄部分負(fù)責(zé)接收日志系統(tǒng)客戶端發(fā)送來的日志消息、日志對象的管理等工作。下面詳細(xì)描述了日志記錄部分的設(shè)計要點:
1.日志記錄器的管理
系統(tǒng)通過保持多個 Logger 對象的方式來進(jìn)行日志記錄的分類。每一個 Logger 對象代表一類日志分類。因此, Logger 對象的名稱屬性是其唯一標(biāo)識,通過名稱屬性獲取一個 Logger 對象:
- Logger logger = Logger.getLogger(“LoggerName”);
一般的,使用類名來作為日志記錄器的名稱,這樣做的好處在于能夠盡量減少日志記錄器命名之間的沖突(因為 Java 類使用包名),同時能夠?qū)⑷罩居涗浄诸惖帽M可能的精細(xì)。因此,假定有一 UserManager 類需要使用日志服務(wù),則更一般的使用方式為:
- Logger logger = Logger.getLogger(UserManager.class);
2.日志分級的實現(xiàn)
按照日志目的不同,將日志的級別由低到高分成五個級別:
DEBUG - 表示輸出的日志為一個調(diào)試信息
INFO - 表示輸出的日志是一個系統(tǒng)提示
WARN - 表示輸出的日志是一個警告信息
ERROR - 表示輸出的日志是一個系統(tǒng)錯誤
FATAL - 表示輸出的日志是一個導(dǎo)致系統(tǒng)崩潰嚴(yán)重錯誤
這些日志級別定義在 LoggerLevel 接口中,被日志記錄器 Logger 在內(nèi)部使用。而對于日志系統(tǒng)客戶端則可使用 Logger 類接口對直接調(diào)用并輸出這些級別的日志, Logger 的這些接口描述如下:
- public void debug(String msg); // 輸出調(diào)試信息
- public void info(String msg); // 輸出系統(tǒng)提示
- public void warn(String msg); // 輸出警告信息
- public void fatal(String msg); // 輸出系統(tǒng)錯誤
- public void error(String msg); // 輸出嚴(yán)重錯誤
通過對 Logger 對象上這些接口的調(diào)用,直接為日志信息賦予了級別屬性,這樣為后繼的按照不同級別進(jìn)行輸出的工作奠定了基礎(chǔ)。
3.日志對象信息的獲取
日志對象上包含了一條日志所具備的所有信息。通常這些信息包括:輸出日志的時間、 Java 類、類成員方法、所在行號、日志體、日志級別等等。在 JDK1.4 中可以通過在方法中拋出并且捕獲住一個異常,則在捕捉到的異常對象中已經(jīng)由 JVM 自動填充好了系統(tǒng)調(diào)用的堆棧,在 JDK1.4 中則可以使用 java.lang.StackTraceElement 獲取到每一個堆棧項的基本信息,通過對日志客戶端輸出日志方法調(diào)用層數(shù)的推算,則可以比較容易的獲取到 StackTraceElement 對象,從而獲取到輸出日志時的 Java 類、類成員方法、所在行號等信息。在 JDK1.3 或者更早的版本中,相應(yīng)的工作則必須通過將異常的堆棧信息輸出到字符串中,并分析該字符串格式得到。
2.3日志輸出部分的設(shè)計
日志輸出部分的設(shè)計具有一定的難度,在本文設(shè)計的日志系統(tǒng)中,日志的輸出、多線程的支持、日志系統(tǒng)的擴展性、日志系統(tǒng)的效率等問題都交由日志輸出部分進(jìn)行管理。
1.日志輸出器的繼承結(jié)構(gòu)
在日志的輸出部分采用了二層結(jié)構(gòu),即定義了一個抽象的日志輸出器( AbstractLoggerAppender ) , 然后從該抽象類繼承出實際的日志輸出器。 AbstractLoggerAppender 定義了一系列的對日志進(jìn)行過濾的方法,而具體輸出到存儲媒介的方法則是一個抽象方法,由子類實現(xiàn)。在系統(tǒng)中默認(rèn)實現(xiàn)了控制臺輸出器和文件輸出器兩種,其中控制臺輸出器的實現(xiàn)頗為簡單。
2.文件輸出器的內(nèi)部實現(xiàn)
在日志記錄部分的實現(xiàn)中,并沒有考慮多線程、高效率等問題,因此文件輸出器必須考慮這些問題的處理。在文件輸出器內(nèi)部使用 java.lang.Vector 定 義了一個線程安全的高速緩沖,所有通過日志記錄部分分派到文件輸出器的日志被直接放置到該高速緩沖當(dāng)中。同時在文件輸出器內(nèi)部定義一個工作線程,負(fù)責(zé)定期 將高速緩沖中的內(nèi)容保存到文件,在保存的過程中同時可以進(jìn)行日志文件的備份等工作。由于采用了高速緩沖的結(jié)構(gòu),很顯然日志客戶端的調(diào)用已經(jīng)不再是一個同步 調(diào)用,從而不再會需要等到文件操作后才返回,提高的系統(tǒng)調(diào)用的速度。該原理 如圖 3 所示:
圖 3 ,文件輸出器內(nèi)部結(jié)構(gòu)
2.4設(shè)計難點
通過上述設(shè)計,一個具有良好擴展能力的高性能Java日志架就已經(jīng)具有了一定的雛形。在設(shè)計過程中幾個難點問題需要進(jìn)一步反思。
一、是否整個系統(tǒng)應(yīng)當(dāng)采用完全異步的結(jié)構(gòu),通過類似于消息機制的方式來進(jìn)行由日志客戶端發(fā)送日志給日志系統(tǒng)。這種方式可以作為日志系統(tǒng)框架另一種運行方式,在后繼設(shè)計中加以考慮。
二、在文件輸出器中可以看到,目前雖然可以擴展多個日志輸出器,但是目前提供的抽象類中僅僅提供了對日志的過濾機制,而沒有提供的緩存機制,目前的緩存機制被放在文件輸出器中實現(xiàn),因此在未來的進(jìn)一步設(shè)計中,可以將文件輸出器中的緩存機制上移到抽象類當(dāng)中。
2.5設(shè)計模式
在設(shè)計過程中我們特別注意使用了數(shù)個經(jīng)典的設(shè)計模式。如: Logger 對象的創(chuàng)建使用了工廠方法模式( Factory Method )、由 AbstractLoggerAppender 和 ConsoleAppender 以及 FileAppender 構(gòu)成了策略模式( Strategy ),除此以外,還大量使用了單例模式( Singleton )。在設(shè)計中適當(dāng)運用設(shè)計模式能夠加快設(shè)計進(jìn)度、提高設(shè)計質(zhì)量。
【編輯推薦】