雖然開源社群有很多優秀的日誌框架,但我們學習標準的java日誌框架是為了更好的理解其他框架啊(近期專案要用ELK)
看自己以前寫的Log4J簡直不忍直視啊啊啊啊,那時還感覺自我良好
1. 為什麼要使用日誌
我們都試過在程式碼中插入System.out.println方法來進行除錯吧,當找出問題根源後就把插入的print語句刪除,若又出現問題則需再次插入這些語句,如此反覆。那麼日誌API就是為了解決這個問題而設計的,使用日誌的優勢:
- 可隨時開閉日誌記錄,還能分級別篩選日誌,並且保留日誌程式碼開銷很小
- 日誌可簡單地被定向到控制檯顯示,檔案儲存,或者網路傳輸
- 日誌可格式化其記錄的格式
- 日誌可由配置檔案控制
- 日誌利於日後錯誤的定位
2. Logger
java有標準的日誌系統,在java.util.logging包下。因為它不太好用,就出現了各種補充的日誌框架,其實我看著也還行,能夠應付我的日常使用了
2.1 示例
看不懂沒關係,碼入下面的程式就可以看到日誌記錄的情況了
public class loggerTest {
public static void main(String[] args) {
// 1. 獲得一個全域性的日誌記錄器
Logger global = Logger.getGlobal();
// 2. 日誌有七個級別,從高到低分別是:Sever、Warning、Info、Config、Fine、Finer、Finest
// 預設級別為INFO,意思只輸出前三個級別的記錄
global.info("INFO MSG");
global.warning("WARNING MSG");
global.severe("SERVE MSG");
// 3. 可通過setLevel來設定日誌級別,來限制其他級別的記錄
global.setLevel(Level.WARNING);
}
}
// 控制檯輸出
// 七月 23, 2021 8:57:17 下午 logging.loggerTest main
// 資訊: INFO MSG
// 七月 23, 2021 8:57:17 下午 logging.loggerTest main
// 警告: WARNING MSG
// 七月 23, 2021 8:57:17 下午 logging.loggerTest main
// 嚴重: SERVE MSG
2.2 日誌的記錄器
記錄器是用來 "記錄"、定位日誌記錄的,一般我們不想把所有的日誌都記錄到一個全域性記錄器上,那麼我們就可以自定義一個記錄器
public class loggerTest {
// 未被任何變數引用的日誌記錄器可能被垃圾回收掉,所以採用了靜態變數的方式
private static final Logger myLogger = Logger.getLogger("com.howl.logger.loggerTest");;
public static void main(String[] args) {
myLogger.info("this is my logger msg");
}
}
日誌的記錄器有類似於
包名繼承
的層次結構,父記錄器設定了日誌級別,那麼子記錄器就會繼承這個級別,所以日誌框架的記錄器命名都以類名限定
2.3 日誌配置
java有個叫日誌管理器的東西專門來管配置的,java9的配置檔案是在 jre/conf/logging.properties。日誌管理器在虛擬機器啟動時就初始化,就是在main方法執行之前
我們可以在啟動專案時就指定日誌的配置檔案:java -Djava.util.logging.config.file=新檔名
也可在專案執行時用System.setProperty("java.util.logging.config.file", file)指定配置檔案,並LogManager.getLogManager().readConfiguration()重新初始化日誌管理器生效配置(食用配置檔案形式不好,其他日誌框架的配置在專案根目錄,會自動讀取的)
2.4 日誌的處理器
處理器是用於處理記錄的(也有日誌級別),記錄器有ConsoleHandler、FileHandler、SocketHandler。預設情況下記錄器將記錄發到ConsoleHandler然後輸出,如想輸出到其他地方就新增其他的處理器。具體流程的話,就是記錄器將記錄發給自己的處理器和父記錄器的處理器,全部記錄器的最終祖先是名為 "" 的一個記錄器,它有一個ConsoleHandler,所以預設的日誌記錄都輸出到控制檯
public class loggerTest {
// 靜態變數放垃圾回收
private static final Logger myLogger = Logger.getLogger("com.howl.logger.loggerTest");;
public static void main(String[] args) throws IOException {
// 檔案、控制檯處理器
FileHandler fileHandler = new FileHandler();
ConsoleHandler consoleHandler = new ConsoleHandler();
myLogger.addHandler(consoleHandler); // 這條語句在控制檯輸出了兩次
myLogger.addHandler(fileHandler);
myLogger.info("add two handler");
}
}
// 控制檯輸出
// 七月 23, 2021 9:31:26 下午 logging.loggerTest main
// 資訊: add two handler
// 七月 23, 2021 9:31:26 下午 logging.loggerTest main
// 資訊: add two handler
怎麼會有兩條記錄?
-
fileHander是輸出檔案的(不在控制檯輸出),日誌檔案預設儲存在使用者目錄下的javaN.log中,其中N是唯一編號,預設格式為XML
-
上面說的myLogger發給自己處理器consoleHandler輸出,也會發給父處理器輸出,所以有兩條,可配置userParentHandlers = false,取消使用父處理器
2.5 日誌的過濾器
記錄器,處理器只能根據日誌級別來過濾,而過濾器則更加自由多樣化。我們需要實現Filter介面(注意是Logger下的介面)然後將其交給記錄器(是記錄器啊,下面標題2.6的才是交給處理器)
public class loggerTest {
private static final Logger myLogger = Logger.getLogger("com.howl.logger.loggerTest");;
public static void main(String[] args) throws IOException {
// 過濾器
Filter filter = new Filter() {
@Override
public boolean isLoggable(LogRecord record) {
return record.getMessage().contains("HAHA"); // 記錄包含了HAHA就不過濾
}
};
myLogger.setFilter(filter);
myLogger.info("add two handler");
myLogger.info("i am HAHA");
}
}
// 控制檯輸出
// 七月 23, 2021 9:43:27 下午 logging.loggerTest main
// 資訊: i am HAHA
2.6 日誌的格式化器
格式化器顧名思義是用來格式化記錄的,看需要生成什麼樣格式的記錄,我的話就在日誌前加點東西就好了。也是需要實現format介面的,當然記錄的格式化操作是交給處理器的
public class loggerTest {
private static final Logger myLogger = Logger.getLogger("com.howl.logger.loggerTest");;
public static void main(String[] args) throws IOException {
// 格式化器
Formatter formatter = new Formatter() {
@Override
public String format(LogRecord record) {
return "這裡是格式化器: "+ record.getMessage() + "\n\n";
}
};
ConsoleHandler consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(formatter);
myLogger.addHandler(consoleHandler);
myLogger.info("i am HAHA");
}
}
// 控制檯輸出
// 這裡是格式化器: i am HAHA
//
// 七月 23, 2021 9:52:58 下午 logging.loggerTest main
// 資訊: i am HAHA
3. java日誌的發展史
- Apache 的 log4j 日誌框架最早出現(可用配置檔案管理日誌,並動態載入)
- java1.4 後面才新增的標準日誌庫 java.util.logging(JUL)
- Apache 推出日誌門面Apache Commons Logging(JCL,提供了一套日誌介面,相容上面二者)
- 再然後 JCL 的作者弄了個新的日誌門面 slf4j,並提供了其元件實現 logback
- 最後 Apache 重寫log4j,推出 log4j2
- 因為 slf4j 門面後面才出現,所以推出了各種
補丁
使其相容 JCL 的介面,看著好複雜
日誌門面 | 元件實現 |
---|---|
JCL、slf4j | log4j、log4j、logback、JUL |
使用框架需選一個日誌門面,然後再選擇個門面的實現,不選擇實現的話預設使用 java 的標準庫
4. 專案中為什麼不使用JUL
筆者還沒在專案中實際用過日誌框架,體會到的不多,目前只知道 JUL 的配置管理器實屬敗筆~ 。等筆者搭完這次專案用到的ELK之後再慢慢體會把