OK,現在我們來研究下Java相關的日志。
日志記錄是應用程序運行中必不可少的一部分。具有良好格式和完備信息的日志記錄可以在程序出現問題時幫助開發人員迅速地定位錯誤的根源。對於開發人員來說,在程序中使用日志API記錄日志並不復雜,不過遵循一些最佳實踐可以更好的利用日志。本系列介紹了在Java程序中記錄日志的最佳實踐,同時也介紹了如何使用開源軟件對日志進行聚合和分析。
對於現在的應用程序來說,日志的重要性是不言而喻的。很難想象沒有任何日志記錄功能的應用程序運行在生產環境中。日志所能提供的功能是多種多樣的,包括記錄程序運行時產生的錯誤信息、狀態信息、調試信息和執行時間信息等。在生產環境中,日志是查找問題來源的重要依據。應用程序運行時的產生的各種信息,都應該通過日志 API 來進行記錄。很多開發人員習慣於使用 System.out.println、System.err.println 以及異常對象的 printStrackTrace 方法來輸出相關信息。這些使用方式雖然簡便,但是所產生的信息在出現問題時並不能提供有效的幫助。這些使用方式都應該改為使用日志 API。使用日志 API 並沒有增加很多復雜度,但是所提供的好處是顯著的。
盡管記錄日志是應用開發中並不可少的功能,在 JDK 的最初版本中並不包含日志記錄相關的 API 和實現。相關的 API(java.util.logging 包,JUL)和實現,直到 JDK 1.4 才被加入。因此在日志記錄這一個領域,社區貢獻了很多開源的實現。其中比較流行的包括 log4j 及其后繼者 logback。除了真正的日志記錄實現之外,還有一類與日志記錄相關的封裝 API,如 Apache Commons Logging 和 SLF4J。這類庫的作用是在日志記錄實現的基礎上提供一個封裝的 API 層次,對日志記錄 API 的使用者提供一個統一的接口,使得可以自由切換不同的日志記錄實現。比如從 JDK 的默認日志記錄實現 JUL 切換到 log4j。這類封裝 API 庫在框架的實現中比較常用,因為需要考慮到框架使用者的不同需求。在實際的項目開發中則使用得比較少,因為很少有項目會在開發中切換不同的日志記錄實現。本系列博客對於這兩類庫都會進行具體的介紹。
值得注意和反思的是:記錄日志只是有效地利用日志的第一步,更重要的是如何對程序運行時產生的日志進行處理和分析。典型的情景包括當日志中包含滿足特定條件的記錄時,觸發相應的通知機制,比如郵件或短信通知;還可以在程序運行出現錯誤時,快速地定位潛在的問題源。這樣的處理和分析的能力對於實際系統的維護尤其重要。當運行系統中包含的組件過多時,日志對於錯誤的診斷就顯得格外重要。
- 日志的原理
假如我們現在沒有日志系統,那么我們只能system.out.println來輸出我們的相關代碼來監控我們的項目運行情況了。OK,現在我們來寫一個例子:
package org.linkinpark.commons.logtest; public class Linkin { public static void main(String[] args) { log(); } public static void log() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { System.out.println("開始輸出日志:第" + i + "*" + j + "次!"); } } } }
2次循環后我們在控制台輸出了i*j的效果,這種日志方式非常簡單,但是缺點也非常明顯。這種輸出是不可控制的,對於較復雜的程序,所有的信息都將輸出到屏幕,日志的可讀性非常差。而且輸出到屏幕需要消耗資源,大量的io操作會使程序運行變
慢。那么我們現在來改進一下,代碼如下:
package org.linkinpark.commons.logtest; public class Linkin { private static boolean flag = true; public static void main(String[] args) { log(); } public static void log() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (flag) { System.out.println("開始輸出日志:第" + i + "*" + j + "次!"); } } } } }
上面的代碼沒什么好解釋的,我們通過了一個旗標來控制日志的輸出。雖然這個時候日志的輸出已經可控了,但是也只有2個級別,就是輸出與不輸出。flag旗標為false時不輸出,flag旗標是true時輸出。
Log4j等日志工具也都是這個原理,但是增加了很多的輸出級別。輸出級別,輸出樣式,輸出目的地都可以在配置文件中配置,而不是寫4在我們的程序中,增加了靈活性。