目錄
1.如果slf4j裏引入了多種連接器和底層實現.那麼真正執行的是誰?
2.項目日誌包只加載這些,且沒有配置文件,請問Spring的debug運行日誌會顯示出來嗎?底層實現是哪個日誌系統?
4.有些依賴源碼裏直接使用的log4j,而我現在希望由slf4j+logback來控制所有的日誌怎麼辦?
前言:
編寫目的:如果你覺得你的項目裏日誌系統像不同動物的屎一樣混在一起,那麼這篇文章是你所需要的
閱讀要求:本文的讀者要求至少使用過一段時間日誌系統。對於沒有使用過的,不建議你看。
其他:歡迎轉載,但請註明出處。
分類
目前JAVA流行的日誌系統有
Java.util.Logger 以後簡稱javaLog |
log4j |
logback |
NOPlogger,以後簡稱Nop |
slf4j |
common-logger 以後簡稱jcl |
介紹
以上6個日誌系統,其中slf4j和jcl是接口是規則,相當於jdbc,其他4個是具體實現。
也就是說如果只引入了slf4j和jcl,那麼日誌系統是根本沒有卵用的。
但是你可以不引入slf4j和jcl,而只引入其他實現,那麼實現日誌是可以的。(除了logback)
接下來逐一介紹。
javaLog
這個是JDK1.4的時候增加的日誌類。使用起來很簡單。看代碼,內容很簡單,打印了兩行
import java.util.logging.Logger;
public class JDKLog {
public static void main(String[] args) {
Logger logger = Logger.getLogger("aaaa");
System.out.println(logger.getLevel());//獲取當前Log的等級
logger.info("111111");
logger.info("22222");
}
}
再看下現象,這個現象很有趣,所以要截圖。
如果你用過tomcat,就知道這裏的打印信息和tomcat默認打印的信息簡直一摸一樣。說明tomcat默認使用的就是JavaLog,只是日誌等級默認卻是null。
javalog使用好的話比較麻煩且基本概念落伍。
log4j
apache組織的日誌系統。是最火爆的,看下如何使用
import org.apache.log4j.Logger;
public class Log4jLog {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Log4jLog.class);
logger.info("Log4j");
}
}
如果只是這樣的話,那麼絕對打印不了內容,只會打印紅色的警告提示,因爲對於Log4j來說,配置文件是必須的。
你可以加上一個log4j.properties,然後再打印就打出來了。
logback
logback是一個log4j作者二次開發的一個更爲高效快速的日誌系統。但是它不能單獨使用,因爲它沒有入口,你必須使用接口來使用它,比如slf4j或者jcl
我們接下來用slf4j來看logback如何使用的,你需要這樣作。
引入且只引入以下三個包:slf4j - api.jar,logback - core.jar 和logback - classic.jar。(不要混有其他日誌包,原因在slf4j裏面講。)
然後代碼這樣寫,先不要運行。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBackLog {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogBackLog.class);
logger.info("aaaaaaaa");
System.out.println(logger.getClass());
}
}
如果你能認真一些,就能發現這個測試類裏並沒有引入logback的類,很奇怪的事情!不過還有更奇怪的事情,接下來請你運行。
18:06:08.894 [main] INFO com.bai.momo.LogBackLog - aaaaaaaa
class ch.qos.logback.classic.Logger
打印了日誌,而且打印了logger的className,竟然從org.slf4j.Logger變成了 class ch.qos.logback.classic.Logger。
原因暫時不說,在slf4j裏面講。
現在你還有更需要注意的事情!
請把代碼裏的logger.info改爲logger.debug,再次運行。
你會發現日誌還是打印了。如果你瞭解一些關於日誌級別的有關信息,那麼你應該知道本例中的logger的級別就是DEBUG級別,否則打印不出來。
原因在於:logback會先去尋找配置文件logback-test.xml或logback.xml,如果沒有找到,則會使用ch.qos.logback.core.ConsoleAppender這個默認的控制檯輸出配置。而logback的根記錄器的級別默認是DEBUG。
如果你遇到過控制檯debug日誌無腦刷出,什麼都控制不了,那麼原因就在於此。那麼請配置一個logback.xml來修改格式或者級別即可。一個簡單的info級別的logback.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.padual.com/java/logback.xsd"
debug="false" scan="true" scanPeriod="30 second">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="utf-8">
<pattern>%n%-6p %m %n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
Nop
Nop就是靜默處理,就是什麼都不幹。愛咋咋滴,也不打印也不輸出。
你可以找任意一款NopLogger.java的源碼,就會發現它裏面都是空的。
Nop的作用是什麼?在slf4j裏說。
slf4j
重頭戲到了。
slf4j是簡單日誌門面,它不提供具體的實現,只是它有和其他日誌之間的連接器,姑且就叫做連接器吧。如下圖:
先不看JCL.先看藍色和綠色。
slf4j如果底層使用javaLog,需要slf4-jdk.jar
slf4如果底層使用log4j,需要使用slf4-log4j.jar
slf4如果底層使用logback,需要使用logback-classic.jar
而slf4j的核心jar包是slf4j - api.jar。
那麼如果你想使用slf4j,請先導入slf4j - api.jar,然後選擇其中一個底層日誌系統,引入其包以及連接器的jar包。接着如下使用即可。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogBackLog {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogBackLog.class);
logger.info("aaaaaaaa");
System.out.println(logger.getClass());
}
}
這個代碼片和上面講logback的代碼片一摸一樣。這就是接口的魅力,如果你想更換底層日誌系統,只需要更換底層日誌包和連接器包即可。
自動搜尋底層日誌系統的原理就是:其實每一個連接器裏都有一個org.slf4j.impl.StaticLoggerBinder這個類,是相同的包相同的類哦。slf4j會自動搜索類路徑下的org.slf4j.impl.StaticLoggerBinder,如果搜到了某個org.slf4j.impl.StaticLoggerBinder,就加載該StaticLoggerBinder所處的連接器,使用該連接器裏所默認的底層日誌系統。
注意以下幾點:
1.如果你只引入了slf4j - api.jar,而不引入底層實現或連接器,那麼運行上面的代碼將不會打印日誌,而log.getClass的結果是NopLogger,Nop作用就是這樣,既然我不知道咋辦,那我就啥都不幹。
2.如果你更換了底層實現或連接器,那麼logger,getClass將會改變,因爲具體實現已經改變。
3.如果你更換成了log4j或者logback,請附帶上她們的log4j.properties或logback.xml。
4.如果你引入了多種底層實現或連接器,那個現象,呵呵了~~~~~~解釋一下,slf4j是嚴格不推薦你這樣做的,但是現在的系統都有各種依賴,而依賴裏的日誌系統又不盡相同,所以有時我們也沒有辦法。該怎麼做呢?請看問答
jcl
指的是org.apache.common-logging.jar包。
是apache早期的項目,2000年左右的時候就有了,期間也更新過幾次。
它也是個日誌的門面系統,同樣的也有連接器,但最重要的是它的配置文件,說到這裏,有同學可能會奇怪,爲什麼我一直在用common-logging.jar,而不需要配置文件,卻能照樣打印使用啊?
說來話長,請看jcl目前的搜索底層日誌實現的執行流程,以下流程,順序執行,如果匹配,則立即終止:
1.查找配置文件commons-logging.properties。如果有,看裏面配置了什麼底層實現。
2.查找System.getProperties("org.apache.commons.logging.LogFactory"),如果不是null,看結果代表了什麼底層實現.
3.查找所有jar包的註冊信息,如果裏面有META-INF/services/org.apache.commons.logging.LogFactory這個文件,讀取該文件的第一行即是配置了底層實現.
4.如果有log4j.jar.則設置爲log4j爲底層實現
5.如果是java1.4以後,則設置爲javaLog
6.使用自帶的默認的SimpleLog
而我們平時使用jcl一般都是配合Log4j使用,都是到了第四步終止。
而且即使沒有log4j,還有第五步使用javaLog呢!所以使用JCL時完全不用擔心的。
第三步則是jcl連接器的事情,如果有連接器,那麼log4j就沒有用了。
問答:
1.如果slf4j裏引入了多種連接器和底層實現.那麼真正執行的是誰?
我只知道現象,哪個連接器先加載,就執行誰.因爲slf4j搜索連接器會搜索所有的連接器,然後放到一個數組裏.但是好像默認取數組[0]作爲真正的日誌實現.
2.項目日誌包只加載這些,且沒有配置文件,請問Spring的debug運行日誌會顯示出來嗎?底層實現是哪個日誌系統?
slf4j - api.jar,
common-logging.jar,
log4j.jar,
logback-core.jar,
logback-classic.jar,
jcl-over-slf4j.jar
debug日誌會顯示出來,底層是logback
原因是:spring使用jcl作爲日誌門面,jcl會按照搜索順序,在第三步停止,因爲jcl-over-slf4j.jar是將slfj作爲jcl的底層實現d的連接器,而slf4j啓動起來也會搜索真正的底層實現,所以最終只搜到了logback的連接器(缺少log4到slf4j的連接器)。而logback.xml沒有配置出來,所以會使用默認的控制檯打印且默認級別是debug.
實際上就有了一種橋接的作用.
3.我們講slf4j時那個連接器圖裏,還有個slf4j-jcl的連接器的jar,那個是將jcl作爲slfj的底層實現的連接器,和jcl-over-slf4j.jar作用正好相反,如果將該jar投入到第二題環境裏面。然後將logback-classic.jar刪除掉,則會發生什麼事情?會不會發生slf4j和jcl循環調用最終導致棧溢出呢?
自己去試試吧!
4.有些依賴源碼裏直接使用的log4j,而我現在希望由slf4j+logback來控制所有的日誌怎麼辦?
jcl-over-slf4j.jar是jcl橋接到slf4j的.
log4j-over-slf4j.jar是log4j橋接到Slf4j裏面的.
當然也有javaLog橋接到Slf4裏面的.
切記使用這種模式的時候,小心自循環調用.
最優使用
強烈推薦slf4j作爲門面,然後使用logback作爲底層實現,因爲logback十分高效,當然你可以隨時切換底層實現。
由於一些依賴使用了jcl,那麼請添加jcl-over-slf4j.jar,將jcl的日誌系統實際上由slf4j把控。
結語:
寫作原因是因爲自己的項目裏引入多種依賴,導致日誌系統各種靈異事件,網上教程,也都是讀起來無味之極,索性便花了兩天略讀源碼,又去官網看文檔,大抵才瞭解一些。
寫了4個半小時,酣暢淋漓。
煎炒烹炸,盛盤出來,以饗諸君。