JDK日誌架構解讀

來源:互聯網
上載者:User
日誌是什麼。說穿了不就是一堆System.out.println() 嗎。記得剛學java的時候,還不會調試,於是就在關鍵位置使用System.out.println()列印變數的值。有了日誌程式之後, 本質上還是一樣的,你還得在需要的地方手動調用日誌程式的API,比如常見的log.info()。但是使用日誌程式的好處是,輸出被格式化了,顯得更加規範,以及可以方便的將日誌匯入到其他目的地比如檔案中。
      相關的類和介面位於java/util/logging包中,這個架構記錄日誌的思路是這樣的:主要包括Loggor和Appender(又叫Handler)兩部分(其他的是細節先不管),前者嵌入到應用中供應用調用,接收日誌,形式如log.info();然後把接收到的日誌轉寄給Appender處理,後者將對日誌進行格式化然後輸出到對應的目的地。一個Logger可同時對應多個Appender,每個Appender對應一個具體的輸出目的地。盜張圖放下面:

在代碼中的樣本如下:
public static void main(String[] args) {        ConsoleHandler handler = new ConsoleHandler();        handler.setLevel(Level.FINE);        FileHandler fileHandler = new FileHandler("/home/tt.txt", true);        fileHandler.setLevel(Level.FINER);        Logger logger =  Logger.getLogger("W");        logger.setLevel(Level.ALL);        logger.addHandler(handler);        logger.addHandler(fileHandler);        logger.log(Level.INFO, "hehe"); }

擷取一個Logger對象的唯一方法就是使用Logger的靜態方法getLogger,同時傳入一個String類型的參數作為這個對象的名字。由於可能在各個具體類中使用Logger,所以對其起名一般是直接使用該類的類名,這樣的好處是可以避免重複。因為產生Logger對象時,會首先根據名字去尋找是否已經存在了同名的Logger,有的話直接返回它,否則才去產生新的對象。由於每個類使用Logger的需求不同,要進行個人化配置,所以盡量不使用同一個Logger。
Appender對象是單獨定義的,然後作為入參傳遞給Logger對象的addHandler方法。這裡分別建立了一個ConsoleHandler對象和一個FileHandler對象,從名字可以看出前者將日誌輸出到標準輸出上,後者把日誌輸出到指定的檔案中去。當然也可以定義多個ConsoleHandler或FileHandler對象,然後添加到這個Logger對象上(雖然對於ConsoleHandler沒啥意義),畢竟一個Logger可同時對應多個Appender。
另外代碼中還涉及到了記錄層級,對日誌進行分級是一個挺好的想法,目的是便於進行管理吧,這種思維方式很“西方化”。jdk中,預設有以下幾個層級的日誌:OFF,SEVERE,WARNING,INFO,CONFIG,FINE,FINER,FINEST,ALL,等級依次降低,對應的日誌越來越詳細,重要程度也越來越低。就是說,OFF將什麼日誌都不要,而ALL將輸出所有日誌。一條日誌屬於哪個層級是開發人員在調用Logger的log方法時候自己指定的,該方法的第一個參數要求指定一個日誌的層級,第二個參數才是具體的日誌內容。
無論是Logger還是Appender都可以有自己的過濾器,用來過濾自己向下遊輸出的日誌。過濾的原則就是只輸出等級高於或等於配置層級的日誌,比如配置的層級是INFO,那麼層級低於INFO的日誌將不會發送到下遊。代碼中,無論是Logger還是Appender都是調用setLevel方法來設定過濾層級的。

這麼一圈下來,為了打一個"hehe",代碼就過於臃腫了,遠沒有System.out.println("hehe")簡潔高效。我隨便debug了一下原始碼,本質上最後還是一樣的套路:

首先Logger.log()方法中的核心代碼:

while (logger != null) {            final Handler[] loggerHandlers = isSystemLogger                ? logger.accessCheckedHandlers()                : logger.getHandlers();            for (Handler handler : loggerHandlers) {                handler.publish(record);}

其邏輯就是迴圈所有的Appender,並調用其publish方法。這個方法最終調用了StreamHandler類的publish方法,這個方法的核心就是:

try {            if (!doneHeader) {                writer.write(getFormatter().getHead(this));                doneHeader = true;            }            writer.write(msg);}
而這個writer又是誰呢。對於ConsoleHandler而言:

public ConsoleHandler() {        sealed = false;        configure();        setOutputStream(System.err);        sealed = true;}


於是jdk的日誌架構為開發人員提供了設定檔,以減少代碼量。假如對Logger的個人化要求不高,或者說程式中的大部分Logger是一樣的套路,那麼可以採用設定檔來搞定日誌;需要個人化配置的就自己擼代碼,代碼的優先順序高於設定檔。設定檔名為logging.properties,位於${JAVA_HOME}/jre/lib目錄中,內容如下:

handlers= java.util.logging.ConsoleHandler.level= INFO# default file output is in user's home directory.java.util.logging.FileHandler.pattern = %h/java%u.logjava.util.logging.FileHandler.limit = 50000java.util.logging.FileHandler.count = 1java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter# Limit the message that are printed on the console to INFO and above.java.util.logging.ConsoleHandler.level = INFOjava.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter# For example, set the com.xyz.foo logger to only log SEVERE messages:com.xyz.foo.level = SEVERE
那麼這個設定檔做了哪些事呢。第一:為每個非個人化的Logger指定了它將擁有哪類Appender(每類只能有一個),這通過handlers屬性配置;第二:指定了各個Appender類的具體設定,比如記錄層級,日誌格式等;第三:指定了各個Logger的記錄層級,這通過Logger的名字的.level屬性指定的,就像例子中那樣,不知道能不能指定其他的屬性,原始碼沒看完。

最後有一個全域的記錄層級配置:
.level= INFO
這個配置只有在某個Logger或Appender沒有自己配置層級時才會起作用;然而若Logger或Appender有了自己的配置,那麼將會覆蓋全域配置,比如下面這句:
java.util.logging.ConsoleHandler.level = INFO

需要注意的是,這個設定檔是預設的,也就是你可以定義自己的個人化設定檔,然後在運行你的Java程式時,使用java.util.logging.config.file參數來指定檔案的位置,logging.properties也給出了例子:
java -Djava.util.logging.config.file=myfile 

這也說明了一個問題,設定檔是針對整個Java程式的,比如Tomcat就是一個java程式,在其上啟動並執行各個Web應用只是這個程式的一部分,所以各個Web應用也將共用這個日誌的配置。但是如果一個Web應用想擁有自己獨特的日誌配置的話就很麻煩,這是JDK內建的日誌架構滿足不了的,所以Tomcat開發了自己的日誌系統。
 
最後說一下Layouts,算了還是不說了。。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.