欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Java日志框架slf4j作用及其實(shí)現(xiàn)原理

 更新時(shí)間:2018年03月23日 11:02:53   作者:五月的倉頡  
日志記錄是應(yīng)用程序運(yùn)行中必不可少的一部分。這篇文章主要介紹了淺談Java日志框架slf4j作用及其實(shí)現(xiàn)原理,SLF4J是一個(gè)日志框架抽象層,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

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

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

簡(jiǎn)單回顧門面模式

slf4j是門面模式的典型應(yīng)用,因此在講slf4j前,我們先簡(jiǎn)單回顧一下門面模式,

門面模式,其核心為外部與一個(gè)子系統(tǒng)的通信必須通過一個(gè)統(tǒng)一的外觀對(duì)象進(jìn)行,使得子系統(tǒng)更易于使用。用一張圖來表示門面模式的結(jié)構(gòu)為:

門面模式的核心為Facade即門面對(duì)象,門面對(duì)象核心為幾個(gè)點(diǎn):

  1. 知道所有子角色的功能和責(zé)任
  2. 將客戶端發(fā)來的請(qǐng)求委派到子系統(tǒng)中,沒有實(shí)際業(yè)務(wù)邏輯
  3. 不參與子系統(tǒng)內(nèi)業(yè)務(wù)邏輯的實(shí)現(xiàn)

大致上來看,對(duì)門面模式的回顧到這里就可以了,開始接下來對(duì)SLF4J的學(xué)習(xí)。

我們?yōu)槭裁匆褂胹lf4j

我們?yōu)槭裁匆褂胹lf4j,舉個(gè)例子:

我們自己的系統(tǒng)中使用了logback這個(gè)日志系統(tǒng)

我們的系統(tǒng)使用了A.jar,A.jar中使用的日志系統(tǒng)為log4j

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

這樣,我們的系統(tǒng)就不得不同時(shí)支持并維護(hù)logback、log4j、slf4j-simple三種日志框架,非常不便。

解決這個(gè)問題的方式就是引入一個(gè)適配層,由適配層決定使用哪一種日志系統(tǒng),而調(diào)用端只需要做的事情就是打印日志而不需要關(guān)心如何打印日志,slf4j或者commons-logging就是這種適配層,slf4j是本文研究的對(duì)象。

從上面的描述,我們必須清楚地知道一點(diǎn):slf4j只是一個(gè)日志標(biāo)準(zhǔn),并不是日志系統(tǒng)的具體實(shí)現(xiàn)。理解這句話非常重要,slf4j只提做兩件事情:

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

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

為了更理解slf4j,我們先看例子,再讀源碼,相信讀者朋友會(huì)對(duì)slf4j有更深刻的認(rèn)識(shí)。

slf4j應(yīng)用舉例

上面講了,slf4j的直接/間接實(shí)現(xiàn)有slf4j-simple、logback、slf4j-log4j12,我們先定義一個(gè)pom.xml,引入相關(guān)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>

寫一段簡(jiǎn)單的Java代碼:

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

接著我們首先把上面pom.xml的第30行~第49行注釋掉,即不引入任何slf4j的實(shí)現(xiàn)類,運(yùn)行Test方法,我們看一下控制臺(tái)的輸出為:

看到?jīng)]有任何日志的輸出,這驗(yàn)證了我們的觀點(diǎn):slf4j不提供日志的具體實(shí)現(xiàn),只有slf4j是無法打印日志的。

接著打開logback-classic的注釋,運(yùn)行Test方法,我們看一下控制臺(tái)的輸出為:

看到我們只要引入了一個(gè)slf4j的具體實(shí)現(xiàn)類,即可使用該日志框架輸出日志。

最后做一個(gè)測(cè)驗(yàn),我們把所有日志打開,引入logback-classic、slf4j-simple、log4j,運(yùn)行Test方法,控制臺(tái)輸出為:

和上面的差別是,可以輸出日志,但是會(huì)輸出一些告警日志,提示我們同時(shí)引入了多個(gè)slf4j的實(shí)現(xiàn),然后選擇其中的一個(gè)作為我們使用的日志系統(tǒng)。

從例子我們可以得出一個(gè)重要的結(jié)論,即slf4j的作用:只要所有代碼都使用門面對(duì)象slf4j,我們就不需要關(guān)心其具體實(shí)現(xiàn),最終所有地方使用一種具體實(shí)現(xiàn)即可,更換、維護(hù)都非常方便。

slf4j實(shí)現(xiàn)原理

上面看了slf4j的示例,下面研究一下slf4j的實(shí)現(xiàn),我們只關(guān)注重點(diǎn)代碼。

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

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);
  }
}

這個(gè)地方第7行是一個(gè)關(guān)鍵,看一下代碼:

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;
}

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

我們不能避免在系統(tǒng)中同時(shí)引入多個(gè)slf4j的實(shí)現(xiàn),所以接收的地方是一個(gè)Set。大家應(yīng)該注意到,上部分在演示同時(shí)引入logback、slf4j-simple、log4j的時(shí)候會(huì)有警告:

這就是因?yàn)橛腥齻€(gè)"org/slf4j/impl/StaticLoggerBinder.class"存在的原因,此時(shí)reportMultipleBindingAmbiguity方法控制臺(tái)輸出語句:

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.");
  }
}

那網(wǎng)友朋友可能會(huì)問,同時(shí)存在三個(gè)"org/slf4j/impl/StaticLoggerBinder.class"怎么辦?首先確定的是這不會(huì)導(dǎo)致啟動(dòng)報(bào)錯(cuò),其次在這種情況下編譯期間,編譯器會(huì)選擇其中一個(gè)StaticLoggerBinder.class進(jìn)行綁定。

最后StaticLoggerBinder就比較簡(jiǎn)單了,不同的StaticLoggerBinder其getLoggerFactory實(shí)現(xiàn)不同,拿到ILoggerFactory之后調(diào)用一下getLogger即拿到了具體的Logger,可以使用Logger進(jìn)行日志輸出。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JavaWeb 使用Session實(shí)現(xiàn)一次性驗(yàn)證碼功能

    JavaWeb 使用Session實(shí)現(xiàn)一次性驗(yàn)證碼功能

    這篇文章主要介紹了JavaWeb 使用Session實(shí)現(xiàn)一次性驗(yàn)證碼功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • java整合微信支付功能詳細(xì)示例

    java整合微信支付功能詳細(xì)示例

    這篇文章主要給大家介紹了關(guān)于java整合微信支付功能的相關(guān)資料,支付是一個(gè)復(fù)雜且測(cè)試起來需要的配置特別復(fù)雜的模塊,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考下
    2023-07-07
  • 淺談使用setBounds()方法需要注意的地方

    淺談使用setBounds()方法需要注意的地方

    下面小編就為大家?guī)硪黄獪\談使用setBounds()方法需要注意的地方。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • Java程序執(zhí)行的全流程

    Java程序執(zhí)行的全流程

    這篇文章主要介紹了Java程序執(zhí)行的全流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • springboot項(xiàng)目部署到k8s上的方法步驟

    springboot項(xiàng)目部署到k8s上的方法步驟

    本文主要介紹了springboot項(xiàng)目部署到k8s上的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • MyBatis中criteria的or(或查詢)語法說明

    MyBatis中criteria的or(或查詢)語法說明

    這篇文章主要介紹了MyBatis中criteria的or(或查詢)語法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • springBoot詳解集成Swagger流程

    springBoot詳解集成Swagger流程

    Swagger是一個(gè)規(guī)范和完整的框架,用于生成、描述、調(diào)用和可視化?Restful?風(fēng)格的?Web?服務(wù)??傮w目標(biāo)是使客戶端和文件系統(tǒng)作為服務(wù)器以同樣的速度來更新。文件的方法、參數(shù)和模型緊密集成到服務(wù)器端的代碼,允許API來始終保持同步
    2022-06-06
  • SpringBoot整合七牛云上傳圖片的示例代碼

    SpringBoot整合七牛云上傳圖片的示例代碼

    本文就來介紹了SpringBoot整合七牛云上傳圖片的示例代碼,用戶在前端上傳圖片后,交由后端處理,上傳至七牛云,感興趣的可以了解一下
    2023-10-10
  • SpringBoot工程搭建打包、啟動(dòng)jar包和war包的教程圖文詳解

    SpringBoot工程搭建打包、啟動(dòng)jar包和war包的教程圖文詳解

    這篇文章主要介紹了SpringBoot工程搭建打包、啟動(dòng)jar包和war包的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 詳解Spring Boot Profiles 配置和使用

    詳解Spring Boot Profiles 配置和使用

    本篇文章主要介紹了詳解Spring Boot Profiles 配置和使用,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-06-06

最新評(píng)論