'slf4j'에 해당하는 글 2건

log4j 를 만든 Ceki Gülcü 는 log4j 를 포함한 다른 logging 시스템들 보다 더 가볍고 빠르게 동작하는 Logback 을 개발하였다.

log4j 와 logback 은 개념적으로 매우 유사하고, log4j 를 사용했던 사용자라면 logback 역시 쉽게 다가갈 수 있다.

System.out.println 와 비교하자면 빠르고, 레벨, 환경 등에 따라 출력 여부를 표시할 수 있다는 점?



Logback 모듈


Logback 은 logback-core, logback-classic, logback-access 세 가지 모듈로 나뉜다.


  • logback-classic 은 log4j 의 향상된 버전에 해당하며, SLF4J API를 기본적으로 구현하여 log4j 이나 JUL 과 같은 다른 로깅 시스템과 쉽게 전환이 가능하다.
  • logback-access 는 Servlet 컨테이너와 통합되어 HTTP 액세스 로그 기능을 제공한다.
  • logback-core 은 위 두 모듈의 기반이 되며, logback 은 이 모듈의 일부인 Joran 이라는 구성 라이브러리에 의존한다. 



Logback 특징


  • logback-classic 모듈의 Logger 클래스가 SLF4J API 를 기본적으로 구현하므로 SLF4J 로거를 호출 할 때 오버 헤드가 발생하지 않는다. 
  • logback-classic 은 설정 파일이 수정되면 자동으로 reload 할 수 있다. 
  • FileAppender 를 비롯한 하위 클래스는 I/O 오류를 정상적으로 복구할 수 있다.
  • 아카이브 된 로그 파일을 자동으로 비동기 압축하며, maxHistory 프로퍼티를 설정하여 오래된 아카이브 로그 파일을 자동으로 삭제할 수 있다.
  • Prudent 모드로 여러 FileAppender 인스턴스로부터 동일한 로그 파일에 안전하게 기록할 수 있다.
  • development, testing, production 같은 다른 환경에 대하여 구성 파일을 중복 구성할 필요없이, 단일 구성 파일에서 조건부 처리를 사용할 수 있다.
    (조건부 처리시 Janino 라이브러리 필요)
  • MDCFilter 를 사용하여 특정 사용자에 대한 Logging Level 지정이 가능하다.
  • SiftingAppender 는 사용자 세션에 따라 로그 파일을 분리할 수 있다.
  • logback 이 exception 을 출력할 때, stack trace 에서 exception 에 개입하는 클래스, 패키지, 패키지 버전을 알 수 있다.
  • logback-Access 모듈은 Jetty, Tomcat 같은 Servlet 컨테이너와 통합되어 풍부하고 강력한 HTTP 액세스 로그 기능을 제공한다.



Logback 요구사항


logback-classic 모듈을 사용하려면, classpath 에 logback-classic.jar 외에도 slf4j-api.jar 와 logback-core.jar 파일이 있어야 한다.

maven 이나 gradle 같은 빌드 툴을 사용할 때는, logback-classic 모듈만 가져오면 slf4j-api.jar, logback-core.jar 파일도 딸려온다.


gradle 예)


dependencies {
    // logback-classic (slf4j-api, logback-core 포함)
    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
}
cs



Logback 구현


package chapters.introduction;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class HelloWorld1 {
 
    public static void main(String[] args) {
 
        Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld1");
        logger.debug("Hello world.");  // 20:49:07.962 [main] DEBUG chapters.introduction.HelloWorld1 - Hello world.
 
    }
}
cs


"Hello world." 라는 debug 레벨의 로그를 출력하는 예제이다.

SLF4J API 에 정의된 Logger 와 LoggerFactory 를 import 하고, Logger 인스턴스를 생성해서 사용했다.

이 예제처럼, Logging 이 필요한 대부분의 경우 logback 클래스를 참조하지 않고 SLF4J API 만으로 구현해야 한다.


만약 logback 수명주기 동안의 로그가 필요하다면 StatusManager 라는 컴포넌트를 이용할 수 있으며, Logback 관련 문제 진단에 매우 유용할 수 있다.

StatusPrinter 클래스의 print() 메소드를 호출하여 logback 의 내부 상태를 출력할 수 있다.


import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
 
...
 
// print internal state
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);
 
/*
12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
12:49:22,076 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:49:22,078 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Setting up default configuration.
*/
cs


위 로그에서 logback 이 구성 파일을 찾지 못하여 기본 정책(ConsoleAppender) 으로 구성되었음을 알 수 있다.

Appender 는 로그를 남길 목적지로, console, files, Syslog, TCP Sockets, JMS 등 다양하게 설정할 수 있다.

에러가 발생하면 logback은 자동으로 내부 상태를 콘솔에 출력한다.



Logback 사용 정리


  1. logback.xml 이나 logback.groovy 같은 구성 설정.
  2. Logging 이 필요한 모든 클래스에 org.slf4j.Logger 인스턴스 생성.
  3. debug(), info(), warn() 및 error() 등 적절한 출력 메소드 호출로 구성된 appender 에 Logging.




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,

개발자들은 어플리케이션의 중심부에 통합 로깅을 구성하여, 개발시 문제점 추적이나 필요한 부분을 모니터링 하려고 할 것이다.

System.out.println() 을 열심히 붙여 넣으면 로깅이 가능하다.

조금더 진보적인 방법으로는 Logging Framework 를 사용하는 방법이 있다.


Logging Framework 의 종류는 매우 많겠지만, 대부분 사용하는 것은 정해져 있는 듯 하다.

JUL, Log4j, Logback 정도...


Spring Framework 은 기본적으로 spring-core 모듈이 JCL(Jakarta Commons Logging) API 에 의존성을 가지고 있다.

commons-logging 의 역할은 지정된 클래스패스에서 다른 Logging Framework 를 찾아 동일한 API 로 로깅을 구현할 수 있게 한다.

예를 들면 Log4j 같은 Logging Framework 와 연동시켜 logging 을 가능하게 한다.

사용 가능한 Logging Framework 가 없다면 JDK 의 JUL(java.util.logging) 을 기본으로 사용한다.


문제는 commons-logging 이 slf4j 에 비해 비효율적이라는데 있다.

slf4j 는 commons-logging 처럼 다른 Logging Framework 를 찾는 작업을 런타임시 하지 않고, 컴파일 시점에 바인딩하기 때문에 더 효율적이다.

이러한 효율성이 얼마나 체감 가능한지는 모르겠지만 아무튼 그러한 이유로 slf4j 를 많이 사용한다고 한다.

Log4j 도 런타임 오버헤드와 코드 사이즈가 증가하는 단점으로 인해, slf4j API 를 구현하는 Logback 을 주로 사용한다.

JUL, JCL, Log4j 등을 호출하는 외부 라이브러리 이용시에는 slf4j 의 extend 라이브러리(jcl-over-slf4j 등) 를 사용하면 slf4j 로 통합된다.


정리하자면, commons-logging 이나 slf4j 같은 Logging API 를 사용하는 이유는,

JUL 이든 Log4j 든 Logback 이든 개발 도중 Logging Framework 가 바껴도 Logging 에 관련된 코드를 수정할 필요가 없도록 하기 위해서이다.

slf4j + logback 인 logging 을 위해 dependancy 에 추가할 것은 세가지 정도가 되겠다.


  1. slf4j Logging API
  2. logback Logging Framework
  3. Binding to slf4j : jul-over-slf4j / jcl-over-slf4j / log4j-over-slf4j / ... 


Gradle 을 예로 들면, 다음과 같은 형태가 되겠다.


dependencies {
    // logback-classic (slf4j-api, logback-core 포함)
    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
    compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.25'
    compile group: 'org.slf4j', name: 'log4j-over-slf4j', version: '1.7.25'
}
cs


마지막으로 resources 디렉토리에 적당한 logback.xml 나 logback.groovy 파일을 생성/설정한다. ^^




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,