第四步,在 resources 目錄下建立 log4j.properties 檔案,内容如下所示:
### 設定###
log4j.rootLogger = debug,stdout,D
### 輸出資訊到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 輸出DEBUG 級别以上的日志到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
再次運作 Demo 類,你會發現 target 目錄下會生成一個名叫 debug.log 的檔案,内容如下所示:
2020-10-21 15:32:06 [ main:0 ] - [ INFO ] jcl
1
并且可以在控制台看到以下資訊:
[INFO ] 2020-10-21 15:32:06,192 method:com.itwanger.Demo.main(Demo.java:12)
jcl
2
仔細對比一下,你就會發現,這次輸出的格式和之前不一樣,這就是因為 Log4j 和 JUL 的日志格式不同導緻的。
另外,你有沒有發現?我們并沒有改動測試類 Demo,它裡面使用的仍然是 JCL 擷取 Log 的方式:
private static Log logger = LogFactory.getLog(Demo.class);
但輸出的格式已經切換到 Log4j 了!
SLF4J 除了提供這種解決方案,綁定 Log4j 替換 JUL 和 JCL;還提供了綁定 Logback 替換 JUL、JCL、Log4j 的方案:
還有綁定 JUL 替換 JCL 和 Log4j 的方案:太強了,有木有?有的話請在留言區敲出 666。
03、SLF4J 比 Log4J 強在哪
SLF4J 除了解決掉以上的痛點,幫助我們的應用程式獨立于任何特定的日志系統,還有一個非常牛逼的功能,那就是 SLF4J 在列印日志的時候使用了占位符 {},它有點類似于 String 類的 format() 方法(使用 %s 等填充參數),但更加便捷,這在很大程度上提高了程式的性能。
衆所周知,字元串是不可變的,字元串拼接會建立很多不必要的字元串對象,極大的消耗了記憶體空間。但 Log4J 在列印帶參數的日志時,隻能使用字元串拼接的方式:
String name = "沉默王二";
int age = 18;
logger.debug(name + ",年紀:" + age + ",是個非常不要臉的程式員");
非常笨重,但加入了 SLF4J 後,這個問題迎刃而解。我們來看一下在 Log4j 項目中加入 SLF4J 的詳細的步驟。
第一步,把 log4j 的依賴替換為 slf4j-log4j12(Maven 會自動引入 slf4j-api.jar 和 log4j.jar):
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
第二步,在 resources 目錄下建立 log4j.properties 檔案,内容和 Log4j 那一篇完全相同:
### 設定###
log4j.rootLogger = debug,stdout,D,E
### 輸出資訊到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 輸出DEBUG 級别以上的日志到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 輸出ERROR 級别以上的日志到=error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
第三步,建立測試類:
package com.itwanger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author 微信搜「沉默王二」,回複關鍵字 PDF
*/
public class Log4jSLF4JDemo {
private static final Logger logger = LoggerFactory.getLogger(Log4jSLF4JDemo.class);
public static void main(String[] args) {
logger.debug("{},是個非常不要臉的程式員","沉默王二");
}
}
看到了吧,使用占位符要比“+”操作符友善的多。并且此時不再需要 isDebugEnabled() 先進行判斷,debug() 方法會在字元串拼接之前執行。
如果隻是 Log4J 的話,會先進行字元串拼接,再執行 debug() 方法,來看示例代碼:
在調試這段代碼的時候,你會發現的,如下圖所示:
這也就意味着,如果日志系統的級别不是 DEBUG,就會多執行了字元串拼接的操作,白白浪費了性能。
注意,阿裡巴巴開發手冊上還有一條「強制」級别的規約:
這是因為如果參數是基本資料類型的話,會先進行自動裝箱(Integer.valueOf())。測試代碼如下所示:
logger.debug("沉默王二,{}歲", 18);
通過反編譯工具就可以看得到:
logger.debug("\u6C89\u9ED8\u738B\u4E8C\uFF0C{}\u5C81", Integer.valueOf(18));
如果參數需要調用其他方法的話,debug() 方法會随後調用。
也就是說,如果不 isDebugEnabled() 的話,在不是 DEBUG 級别的情況下,會多執行自動裝箱和調用其他方法的操作——程式的性能就下降了!
測試類運作的結果和之前 Log4J 的一樣,小夥伴們可以點選連結跳轉到 Log4j 那篇對比下。
04、總結
簡單總結一下這篇文章哈。
1)在使用日志系統的時候,一定要使用 SLF4J 作為門面擔當。
2)SLF4J 可以統一日志系統,作為上層的抽象接口,不需要關注底層的日志實作,可以是 Log4j,也可以是 Logback,或者 JUL、JCL。
3)SLF4J 在列印日志的時候可以使用占位符,既提高了程式性能(臨時字元串少了,垃圾回收的工作量就小),又讓代碼變得美觀統一。
4)小夥伴們如果知道更多秘密的話,建議在留言區貼出來哦。