雖然開源社區有很多優秀的日志框架,但我們學習標准的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之后再慢慢體會把