亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

淺談Java日志框架slf4j作用及其實現原理

2024-07-14 08:40:07
字體:
來源:轉載
供稿:網友

SLF4J是一個日志框架抽象層,底下綁定具體的日志框架,比如說Log4J,Logback,Java Logging API等。SLF4J也有自身的默認實現,但是我們還是主要以日志框架抽象層的身份使用SLF4J。

要使用SLF4J,得包含對"org.slf4j:slf4j-api"的依賴。

簡單回顧門面模式

slf4j是門面模式的典型應用,因此在講slf4j前,我們先簡單回顧一下門面模式,

門面模式,其核心為外部與一個子系統的通信必須通過一個統一的外觀對象進行,使得子系統更易于使用。用一張圖來表示門面模式的結構為:

Java,slf4j,日志框架

門面模式的核心為Facade即門面對象,門面對象核心為幾個點:

  1. 知道所有子角色的功能和責任
  2. 將客戶端發來的請求委派到子系統中,沒有實際業務邏輯
  3. 不參與子系統內業務邏輯的實現

大致上來看,對門面模式的回顧到這里就可以了,開始接下來對SLF4J的學習。

我們為什么要使用slf4j

我們為什么要使用slf4j,舉個例子:

我們自己的系統中使用了logback這個日志系統

我們的系統使用了A.jar,A.jar中使用的日志系統為log4j

我們的系統又使用了B.jar,B.jar中使用的日志系統為slf4j-simple

這樣,我們的系統就不得不同時支持并維護logback、log4j、slf4j-simple三種日志框架,非常不便。

解決這個問題的方式就是引入一個適配層,由適配層決定使用哪一種日志系統,而調用端只需要做的事情就是打印日志而不需要關心如何打印日志,slf4j或者commons-logging就是這種適配層,slf4j是本文研究的對象。

從上面的描述,我們必須清楚地知道一點:slf4j只是一個日志標準,并不是日志系統的具體實現。理解這句話非常重要,slf4j只提做兩件事情:

  1. 提供日志接口
  2. 提供獲取具體日志對象的方法

slf4j-simple、logback都是slf4j的具體實現,log4j并不直接實現slf4j,但是有專門的一層橋接slf4j-log4j12來實現slf4j。

為了更理解slf4j,我們先看例子,再讀源碼,相信讀者朋友會對slf4j有更深刻的認識。

slf4j應用舉例

上面講了,slf4j的直接/間接實現有slf4j-simple、logback、slf4j-log4j12,我們先定義一個pom.xml,引入相關jar包:

<!-- 原文:五月的倉頡http://www.cnblogs.com/xrq730/p/8619156.html --><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">   <modelVersion>4.0.0</modelVersion>   <groupId>org.xrq.log</groupId>   <artifactId>log-test</artifactId>   <version>1.0.0</version>   <packaging>jar</packaging>   <name>log-test</name>   <url>http://maven.apache.org</url>   <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>   </properties>   <dependencies>    <dependency>      <groupId>junit</groupId>       <artifactId>junit</artifactId>       <version>4.11</version>       <scope>test</scope>    </dependency>    <dependency>      <groupId>org.slf4j</groupId>      <artifactId>slf4j-api</artifactId>      <version>1.7.25</version>    </dependency>    <dependency>      <groupId>ch.qos.logback</groupId>      <artifactId>logback-classic</artifactId>      <version>1.2.3</version>    </dependency>    <dependency>      <groupId>org.slf4j</groupId>      <artifactId>slf4j-simple</artifactId>      <version>1.7.25</version>    </dependency>    <dependency>      <groupId>log4j</groupId>      <artifactId>log4j</artifactId>      <version>1.2.17</version>    </dependency>    <dependency>      <groupId>org.slf4j</groupId>      <artifactId>slf4j-log4j12</artifactId>      <version>1.7.21</version>    </dependency>   </dependencies></project>

寫一段簡單的Java代碼:

@Testpublic void testSlf4j() {  Logger logger = LoggerFactory.getLogger(Object.class);  logger.error("123"); }

接著我們首先把上面pom.xml的第30行~第49行注釋掉,即不引入任何slf4j的實現類,運行Test方法,我們看一下控制臺的輸出為:

Java,slf4j,日志框架

看到沒有任何日志的輸出,這驗證了我們的觀點:slf4j不提供日志的具體實現,只有slf4j是無法打印日志的。

接著打開logback-classic的注釋,運行Test方法,我們看一下控制臺的輸出為:

Java,slf4j,日志框架

看到我們只要引入了一個slf4j的具體實現類,即可使用該日志框架輸出日志。

最后做一個測驗,我們把所有日志打開,引入logback-classic、slf4j-simple、log4j,運行Test方法,控制臺輸出為:

Java,slf4j,日志框架

和上面的差別是,可以輸出日志,但是會輸出一些告警日志,提示我們同時引入了多個slf4j的實現,然后選擇其中的一個作為我們使用的日志系統。

從例子我們可以得出一個重要的結論,即slf4j的作用:只要所有代碼都使用門面對象slf4j,我們就不需要關心其具體實現,最終所有地方使用一種具體實現即可,更換、維護都非常方便。

slf4j實現原理

上面看了slf4j的示例,下面研究一下slf4j的實現,我們只關注重點代碼。

slf4j的用法就是常年不變的一句"Logger logger = LoggerFactory.getLogger(Object.class);",可見這里就是通過LoggerFactory去拿slf4j提供的一個Logger接口的具體實現而已,LoggerFactory的getLogger的方法實現為:

public static Logger getLogger(Class<?> clazz) {  Logger logger = getLogger(clazz.getName());  if (DETECT_LOGGER_NAME_MISMATCH) {    Class<?> autoComputedCallingClass = Util.getCallingClass();    if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {      Util.report(String.format("Detected logger name mismatch. Given name: /"%s/"; computed name: /"%s/".", logger.getName(),              autoComputedCallingClass.getName()));      Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");    }  }  return logger;}

從第2行開始跟代碼,一直跟到LoggerFactory的bind()方法:

private final static void bind() {  try {    Set<URL> staticLoggerBinderPathSet = null;    // skip check under android, see also    // http://jira.qos.ch/browse/SLF4J-328    if (!isAndroid()) {      staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();      reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);    }    // the next line does the binding    StaticLoggerBinder.getSingleton();    INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;    reportActualBinding(staticLoggerBinderPathSet);    fixSubstituteLoggers();    replayEvents();    // release all resources in SUBST_FACTORY    SUBST_FACTORY.clear();  } catch (NoClassDefFoundError ncde) {    String msg = ncde.getMessage();    if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {      INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;      Util.report("Failed to load class /"org.slf4j.impl.StaticLoggerBinder/".");      Util.report("Defaulting to no-operation (NOP) logger implementation");      Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");    } else {      failedBinding(ncde);      throw ncde;    }  } catch (java.lang.NoSuchMethodError nsme) {    String msg = nsme.getMessage();    if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {      INITIALIZATION_STATE = FAILED_INITIALIZATION;      Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");      Util.report("Your binding is version 1.5.5 or earlier.");      Util.report("Upgrade your binding to version 1.6.x.");    }    throw nsme;  } catch (Exception e) {    failedBinding(e);    throw new IllegalStateException("Unexpected initialization failure", e);  }}

這個地方第7行是一個關鍵,看一下代碼:

static Set<URL> findPossibleStaticLoggerBinderPathSet() {  // use Set instead of list in order to deal with bug #138  // LinkedHashSet appropriate here because it preserves insertion order  // during iteration  Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();  try {    ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();    Enumeration<URL> paths;    if (loggerFactoryClassLoader == null) {      paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);    } else {      paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);    }    while (paths.hasMoreElements()) {      URL path = paths.nextElement();      staticLoggerBinderPathSet.add(path);    }  } catch (IOException ioe) {    Util.report("Error getting resources from path", ioe);  }  return staticLoggerBinderPathSet;}

這個地方重點其實就是第12行的代碼,getLogger的時候會去classpath下找STATIC_LOGGER_BINDER_PATH,STATIC_LOGGER_BINDER_PATH值為"org/slf4j/impl/StaticLoggerBinder.class",即所有slf4j的實現,在提供的jar包路徑下,一定是有"org/slf4j/impl/StaticLoggerBinder.class"存在的,我們可以看一下:

Java,slf4j,日志框架

Java,slf4j,日志框架

Java,slf4j,日志框架

我們不能避免在系統中同時引入多個slf4j的實現,所以接收的地方是一個Set。大家應該注意到,上部分在演示同時引入logback、slf4j-simple、log4j的時候會有警告:

Java,slf4j,日志框架

這就是因為有三個"org/slf4j/impl/StaticLoggerBinder.class"存在的原因,此時reportMultipleBindingAmbiguity方法控制臺輸出語句:

private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {  if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {    Util.report("Class path contains multiple SLF4J bindings.");    for (URL path : binderPathSet) {      Util.report("Found binding in [" + path + "]");    }    Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");  }}

那網友朋友可能會問,同時存在三個"org/slf4j/impl/StaticLoggerBinder.class"怎么辦?首先確定的是這不會導致啟動報錯,其次在這種情況下編譯期間,編譯器會選擇其中一個StaticLoggerBinder.class進行綁定。

最后StaticLoggerBinder就比較簡單了,不同的StaticLoggerBinder其getLoggerFactory實現不同,拿到ILoggerFactory之后調用一下getLogger即拿到了具體的Logger,可以使用Logger進行日志輸出。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
色狠狠av一区二区三区香蕉蜜桃| 久久久久久免费精品| 久久综合88中文色鬼| 亚洲精品91美女久久久久久久| 国产精品久久久久久久久久尿| 91精品啪在线观看麻豆免费| 在线观看不卡av| 国产亚洲精品久久久久久| 国产主播在线一区| 日韩不卡在线观看| 这里只有精品视频| 亚洲成人网在线| 欧美第一黄色网| 欧美日韩国产精品专区| 一区二区欧美日韩视频| 亚洲精品福利免费在线观看| 日韩久久免费视频| 欧美成人小视频| 国产成人综合精品| 欧美日韩福利在线观看| 日韩av免费看网站| 国产丝袜一区二区三区免费视频| 久久男人av资源网站| 久久精品国产一区二区三区| 97超级碰碰人国产在线观看| 欧美午夜丰满在线18影院| 亚洲欧美中文日韩在线| 国产成人精品一区二区三区| 精品国产自在精品国产浪潮| 成人亚洲综合色就1024| 国产一区视频在线| xxx成人少妇69| 久久偷看各类女兵18女厕嘘嘘| 国产成人久久精品| www.亚洲人.com| www.欧美三级电影.com| 亚洲一区二区三区777| 国产精品久久婷婷六月丁香| 日韩免费不卡av| 亚洲国产精久久久久久久| 成人精品一区二区三区电影免费| 亚洲三级av在线| xxx一区二区| 亚洲精品99久久久久| 亚洲美女精品久久| 日韩激情av在线免费观看| 欧美激情在线狂野欧美精品| 亚洲精品久久久久久久久| 亚洲精品久久久久久久久久久| 国产精品人人做人人爽| 日韩欧美有码在线| 精品久久久国产| 国产精品爽黄69| 97香蕉超级碰碰久久免费软件| 91亚洲人电影| 在线观看精品国产视频| 91av在线免费观看视频| 国产极品jizzhd欧美| 精品爽片免费看久久| 亚洲一区二区中文字幕| 精品国模在线视频| 欧美性视频网站| 成人欧美一区二区三区黑人孕妇| 国产免费一区二区三区在线观看| 亚洲综合在线中文字幕| 2023亚洲男人天堂| 国产一区二区三区在线观看视频| 亚洲国产欧美一区二区三区久久| 成人黄色免费网站在线观看| 亚洲美女av电影| 2019亚洲日韩新视频| 国语自产精品视频在线看一大j8| 欧美激情一级欧美精品| 欧美激情按摩在线| 国产精品久久网| 中文字幕日韩av综合精品| 亚洲第一精品自拍| 国产一区二区三区欧美| 亚洲国产成人精品一区二区| 欧美成人午夜免费视在线看片| 国外成人免费在线播放| 亚洲无亚洲人成网站77777| 在线日韩精品视频| 亚洲aⅴ日韩av电影在线观看| 欧美在线视频一区二区| 韩国一区二区电影| 91精品国产91久久久久久不卡| 色噜噜国产精品视频一区二区| 久久综合九色九九| 日韩精品电影网| 国产成人精品免高潮在线观看| 亚洲欧洲午夜一线一品| 92国产精品久久久久首页| 日韩经典一区二区三区| 日韩极品精品视频免费观看| 九九热精品视频国产| 国产亚洲人成网站在线观看| 久久精品国产一区二区三区| 国产亚洲免费的视频看| 最近2019免费中文字幕视频三| 国产日韩综合一区二区性色av| 综合久久五月天| 深夜福利91大全| 欧美巨猛xxxx猛交黑人97人| 亚洲网站在线观看| 九九九久久久久久| 久久成人国产精品| 亚洲福利在线播放| 国产亚洲精品日韩| 久久久精品一区二区| 色青青草原桃花久久综合| 亚洲自拍欧美另类| 高潮白浆女日韩av免费看| 亚洲最大的av网站| 91久久久久久久| 亚洲欧洲视频在线| 亚洲一区亚洲二区| 中文字幕欧美日韩va免费视频| 2019中文字幕在线免费观看| 久久人体大胆视频| 最近2019中文免费高清视频观看www99| 国产免费一区二区三区香蕉精| 91丨九色丨国产在线| 日韩欧美在线一区| 精品动漫一区二区| 成人久久精品视频| 亚洲mm色国产网站| 国产精品久久久久久婷婷天堂| 亚洲一级一级97网| 久热精品视频在线观看一区| 欧美日韩国产一区二区三区| 91免费视频网站| 亚洲国产一区自拍| 成人情趣片在线观看免费| 国产91精品不卡视频| 中日韩美女免费视频网站在线观看| 亚洲另类激情图| 亚洲欧美日韩久久久久久| 久久久噜噜噜久噜久久| 亚洲精品国产精品国产自| 国产激情久久久| 久久精视频免费在线久久完整在线看| 45www国产精品网站| 97国产suv精品一区二区62| 国产深夜精品福利| 在线观看日韩av| 久久久久久国产免费| 国产精品久久99久久| 欧美亚洲成人免费| 国产成人小视频在线观看| 欧美精品18videosex性欧美| 亚洲美女喷白浆| 国产成+人+综合+亚洲欧洲| 在线精品视频视频中文字幕| 久久人人爽人人爽人人片av高请| 91九色国产视频| 成人黄色av免费在线观看| 国产精品一区二区电影| 亚洲欧美制服丝袜| 久久久久久久久久久久久久久久久久av| 欧美精品xxx| 国内免费精品永久在线视频| 亚洲色图国产精品| 欧美中文字幕在线播放|