자바 로그 프레임 워 크 slf4j 역할 및 구현 원리

11655 단어 Javaslf4j
SLF4J 는 로그 프레임 추상 층 으로 로그 4J,Logback,Java Logging API 등 구체 적 인 로그 프레임 워 크 를 연결 합 니 다.SLF4J 도 기본 구현 이 있 지만 로그 프레임 추상 층 으로 SLF4J 를 사용 합 니 다.
SLF4J 를 사용 하려 면'org.slf4j:slf4j-api'에 대한 의존 도 를 포함해 야 합 니 다.
단순 회고 외관 모드
slf4j 는 외관 모델 의 전형 적 인 응용 이기 때문에 slf4j 를 말 하기 전에 우 리 는 먼저 외관 모델 을 간단하게 살 펴 보 자.
외관 모델 은 그 핵심 은 외부 와 하나의 서브 시스템 의 통신 이 반드시 통 일 된 외관 대상 을 통 해 이 루어 져 야 서브 시스템 이 더욱 쉽게 사용 할 수 있다 는 것 이다.한 장의 그림 으로 외관 모델 의 구 조 를 나타 낸다.

외관 모델 의 핵심 은 Facade 즉 외관 대상 이 고 외관 대상 의 핵심 은 몇 가지 이다.
모든 하위 캐릭터 의 기능 과 책임 을 알 고 있다.
클 라 이언 트 가 보 낸 요청 을 서브 시스템 에 위임 하고 실제 업무 논리 가 없습니다4.567917.서브 시스템 내 업무 논리의 실현 에 참여 하지 않 는 다.
대체로 겉치레 패턴 에 대한 회 고 는 여기까지 하고 다음 SLF4J 에 대한 공 부 를 시작 하면 된다.
우리 가 왜 slf4j 를 사용 해 야 합 니까?
우 리 는 왜 slf4j 를 사용 해 야 합 니까?예 를 들 어:
저희 시스템 에 logback 로그 시스템 을 사 용 했 습 니 다.
우리 시스템 은 A.jar,A.jar 에서 사용 하 는 로그 시스템 을 log4j 로 사 용 했 습 니 다.
우리 시스템 은 B.jar,B.jar 에서 사용 하 는 로그 시스템 을 slf4j-simple 로 사 용 했 습 니 다.
이렇게 되면 우리 시스템 은 logback,log4j,slf4j-simple 세 가지 로그 프레임 워 크 를 동시에 지원 하고 유지 해 야 하기 때문에 매우 불편 합 니 다.
이 문 제 를 해결 하 는 방식 은 어댑터 를 도입 하여 어댑터 가 어떤 로그 시스템 을 사용 할 지 결정 하 는 것 이다.호출 단 이 해 야 할 일 은 로 그 를 인쇄 하 는 것 이지 로 그 를 어떻게 인쇄 하 는 지 에 관심 을 가 질 필요 가 없다.slf4j 나 comons-logging 은 바로 이러한 어댑터 이 고 slf4j 는 본 연구 의 대상 이다.
위의 설명 에서 우 리 는 한 가 지 를 분명히 알 아야 한다.slf4j 는 로그 표준 일 뿐 로그 시스템 의 구체 적 인 실현 이 아니다.이 말 을 이해 하 는 것 은 매우 중요 하 다.slf4j 는 두 가지 일 만 한다.
로그 인터페이스 제공
구체 적 인 로그 대상 을 얻 는 방법 을 제공 합 니 다slf4j-simple,logback 은 모두 slf4j 의 구체 적 인 실현 이다.log4j 는 slf4j 를 직접적 으로 실현 하지 않 지만 전문 적 인 브리지 slf4j-log4j 12 로 slf4j 를 실현 한다.
slf4j 를 더욱 이해 하기 위해 우 리 는 먼저 예 를 보고 소스 코드 를 읽 으 며 독자 친구 들 이 slf4j 에 대해 더욱 깊 은 인식 을 가지 게 될 것 이 라 고 믿는다.
slf4j 응용 예
위 에서 말 했 듯 이 slf4j 의 직접/간접 실현 은 slf4j-simple,logback,slf4j-log4j 12 가 있 습 니 다.우 리 는 먼저 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>
간단 한 자바 코드 쓰기:

@Test
public void testSlf4j() {
  Logger logger = LoggerFactory.getLogger(Object.class);
  logger.error("123");
 }
이 어 우 리 는 먼저 위의 pom.xml 의 30 번 째 줄~49 번 째 줄 을 주석 합 니 다.즉,어떠한 slf4j 의 실현 류 도 도입 하지 않 고 Test 방법 을 실행 합 니 다.콘 솔 의 출력 을 살 펴 보 겠 습 니 다.

로그 의 출력 이 없 는 것 을 보 았 습 니 다.이것 은 우리 의 관점 을 검 증 했 습 니 다.slf4j 는 로그 의 구체 적 인 실현 을 제공 하지 않 고 slf4j 만 로 그 를 인쇄 할 수 없습니다.
이 어 logback-classic 의 설명 을 열 고 Test 방법 을 실행 합 니 다.콘 솔 의 출력 을 살 펴 보 겠 습 니 다.

slf4j 의 구체 적 인 실현 클래스 를 도입 하면 로그 프레임 워 크 출력 로 그 를 사용 할 수 있 습 니 다.
마지막 으로 테스트 를 합 니 다.우 리 는 모든 로 그 를 열 고 logback-classic,slf4j-simple,log4j 를 도입 하여 Test 방법 을 실행 합 니 다.콘 솔 출력 은:

위의 차이 점 은 로 그 를 출력 할 수 있 지만 경고 로 그 를 출력 합 니 다.여러 slf4j 의 실현 을 동시에 도입 한 다음 에 그 중 하 나 를 우리 가 사용 하 는 로그 시스템 으로 선택 하 는 것 을 알려 줍 니 다.
예 를 들 어 우 리 는 중요 한 결론 을 얻 을 수 있다.즉,slf4j 의 역할 이다.모든 코드 가 외관 대상 인 slf4j 를 사용 하면 우 리 는 구체 적 인 실현 에 관심 을 가 질 필요 가 없다.최종 적 으로 모든 곳 에서 구체 적 인 실현 을 사용 하면 되 고 교체,유지 가 매우 편리 하 다.
slf4j 실현 원리
위 에서 slf4j 의 예 시 를 보 았 습 니 다.다음은 slf4j 의 실현 을 연구 하고 우 리 는 중점 코드 에 만 관심 을 가 집 니 다.
slf4j 의 용법 은 일년 내 내 변 하지 않 는"Logger logger=Logger Factory.getLogger(Object.class)"입 니 다.이 를 통 해 알 수 있 듯 이 Logger Factory 를 통 해 slf4j 가 제공 하 는 Logger 인터페이스 의 구체 적 인 실현 일 뿐 입 니 다.Logger Factory 의 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;
}
두 번 째 줄 부터 코드 를 따라 Logger Factory 의 bid()방법 까지 따라 갑 니 다.

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);
  }
}
이곳 의 일곱 번 째 줄 이 관건 입 니 다.코드 를 보 세 요.

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"가 존재 합 니 다.다음 을 볼 수 있 습 니 다.



우 리 는 시스템 에서 여러 개의 slf4j 를 동시에 도입 하 는 것 을 피 할 수 없 기 때문에 받 는 곳 은 set 이다.상부 에서 logback,slf4j-simple,log4j 를 동시에 도입 할 때 경고 가 있 음 을 주의해 야 한다.

이것 은 바로 세 개의'org/slf4j/impl/static LoggerBinder.class'가 존재 하 는 이유 입 니 다.이때 report Multiple Binding Ambiguity 방법 콘 솔 출력 문 구 는 다음 과 같 습 니 다.

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/static LoggerBinder.class'세 개가 동시에 존재 하면 어 떡 하 냐 고 물 을 수도 있다.우선,이것 은 시작 보 고 를 잘못 하지 않 을 것 이 확실 합 니 다.그 다음 에 이러한 상황 에서 컴 파일 하 는 동안 컴 파일 러 는 그 중의 StaticLoggerBinder.class 를 선택 하여 연결 합 니 다.
마지막 으로 StaticLoggerBinder 는 비교적 간단 합 니 다.서로 다른 StaticLoggerBinder 는 getLoggerFactory 가 다 릅 니 다.ILoggerFactory 를 받 은 후에 getLogger 를 호출 하면 구체 적 인 Logger 를 받 을 수 있 습 니 다.Logger 를 사용 하여 로그 출력 을 할 수 있 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기