真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網站制作重慶分公司

Java日志框架slf4j作用是什么-創(chuàng)新互聯(lián)

這篇文章給大家分享的是有關Java日志框架slf4j作用是什么的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

創(chuàng)新互聯(lián)建站是少有的做網站、網站設計、營銷型企業(yè)網站、小程序定制開發(fā)、手機APP,開發(fā)、制作、設計、賣鏈接、推廣優(yōu)化一站式服務網絡公司,從2013年開始,堅持透明化,價格低,無套路經營理念。讓網頁驚喜每一位訪客多年來深受用戶好評

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

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

簡單回顧門面模式

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

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

Java日志框架slf4j作用是什么

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

  1. 知道所有子角色的功能和責任

  2. 將客戶端發(fā)來的請求委派到子系統(tǒng)中,沒有實際業(yè)務邏輯

  3. 不參與子系統(tǒng)內業(yè)務邏輯的實現(xiàn)

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

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

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

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

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

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

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

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

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

  1. 提供日志接口

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

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

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

slf4j應用舉例

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



   4.0.0

   org.xrq.log
   log-test
   1.0.0
   jar

   log-test
   http://maven.apache.org

   
    UTF-8
   

   
    
      junit
       junit
       4.11
       test
    
    
      org.slf4j
      slf4j-api
      1.7.25
    
    
      ch.qos.logback
      logback-classic
      1.2.3
    
    
      org.slf4j
      slf4j-simple
      1.7.25
    
    
      log4j
      log4j
      1.2.17
    
    
      org.slf4j
      slf4j-log4j12
      1.7.21
    
   

寫一段簡單的Java代碼:

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

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

Java日志框架slf4j作用是什么

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

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

Java日志框架slf4j作用是什么

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

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

Java日志框架slf4j作用是什么

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

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

slf4j實現(xiàn)原理

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

slf4j的用法就是常年不變的一句"Logger logger = LoggerFactory.getLogger(Object.class);",可見這里就是通過LoggerFactory去拿slf4j提供的一個Logger接口的具體實現(xiàn)而已,LoggerFactory的getLogger的方法實現(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 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 findPossibleStaticLoggerBinderPathSet() {
  // use Set instead of list in order to deal with bug #138
  // LinkedHashSet appropriate here because it preserves insertion order
  // during iteration
  Set staticLoggerBinderPathSet = new LinkedHashSet();
  try {
    ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
    Enumeration 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的實現(xiàn),在提供的jar包路徑下,一定是有"org/slf4j/impl/StaticLoggerBinder.class"存在的,我們可以看一下:

Java日志框架slf4j作用是什么

Java日志框架slf4j作用是什么

Java日志框架slf4j作用是什么

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

Java日志框架slf4j作用是什么

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

private static void reportMultipleBindingAmbiguity(Set 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實現(xiàn)不同,拿到ILoggerFactory之后調用一下getLogger即拿到了具體的Logger,可以使用Logger進行日志輸出。

感謝各位的閱讀!關于“Java日志框架slf4j作用是什么”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!


文章標題:Java日志框架slf4j作用是什么-創(chuàng)新互聯(lián)
URL鏈接:http://weahome.cn/article/idjoj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部