최근에 Logback 로깅 라이브러리에 대해서 알게되었다.

검색 시 많은 자료가 나오는것을 보니 단순 트랜드가 아닌 현재 실무에서도 많이들 사용하는듯 싶습니다.


우선 Log4j,SLF4J를 만든 사람이 만들었다니 더 좋을 수밖에 없다는 생각이듭니다.

(맘에 안들고 불편했던 부분을 개선하고, 여러 실무 의견들을 반영했을 테니까요.)


좋은점에 대한 정보가 많이 있었으나 우선 제 시점에서 좋겠구나 하는것만 나열 하겠고, 나머지는 직접 검색해보시기 바랍니다.(자료 많네요.)


첫째로, 성능이 약 10배 향상, 메모리 점유율도 낮아졌음

우선 메모리야 요즘 넘처나기도 하고, 현재까지 Log4j 사용하면서 메모리 문제는 없었던 상황이니 잘 와닫지 않고, 모바일 기기의 출현으로 0.01 초의 차이도 의미 있는 수치일수 있으므로 성능 향상은 아주 좋은 장점인것 같습니다.


둘째로, 설정파일 자동으로 Reloading

간혹 개발 및 스테이지에서는 확인이 안되고 운영상황에서만 문제가 발생할때 DEBUG모드로 변경후 모니터링 해봐야 하는 경우가 있습니다.

이럴때 Log4j는 서버를 재기동해야 하는 부담이 있는데 Logback을 사용하므로 인해서 부담이 줄겠네요.


세번째로, "logback-access" 라고 HTTP 디버깅

웹개발자로서 무슨소린지 몰라도 유용할듯 싶은데... 셈플링 해봐야할것 같네요.


네번째로, 자동삭제

서버관리상의 기능이긴 하지만 유용한 기능인것 같습니다.

굳이 배치나 주기적으로 삭제관련 모니터링을 하지 않아도 되니까요.


다섯째로, Prudent mode

다수의 JVM일 경우 하나의 파일에 쌓을수 있다는데..

한장비안에서 쌓을 일은 없을듯 하고, 별도의 장비에서 동일한 NAS경로 셋팅되어 있어도 가능한지 모르것네;;


여섯째로, 분기 스트립트 작성

if,else 와 같은 문법 사용이 가능하므로, 개발, 스테이지, 운영에 따른 처리가 한 파일로 가능하겠군요.

기존에는 파일을 각각 두어서 시스템프로퍼티에 해당 정보를 셋팅해서 선택하게끔 하거나, 배포시 파일명을 변경하곤 하였는데 말이죠. ㅎㅎ


일곱째로, 특정 분류별 로그파일 기록

특정 사용자별로 하고 싶은데.. 가능하려나 모르것네..


여덜째로, Stack Trace 출력

외부 라이브러리에서 발생한 Exception에 대해서 참조되는 라이브러리 버전까지 출력을 해준다네..

전에 버전이 틀린 동일한 라이브라리가 두개 올라가져 있어서 개발,스테이지,운영간에 두개중 참조하는게 틀려서 한참 삽질 했던적 있는데 그러한경우 디버깅이 그나마 쉬워지겠군요.



우선 간단한 셈플링부터.. 나머지는 차차차~


[build.gradle]

apply plugin: 'java'
apply plugin: 'eclipse'

sourceCompatibility = 1.6

repositories {
    mavenCentral()
}


// SLF4J + Logback
List loggerSlf4jAndLogback = [
    "ch.qos.logback:logback-classic:1.0.13",
    "org.slf4j:jcl-over-slf4j:1.7.5"
]

dependencies {
    compile loggerSlf4jAndLogback
}

// commons-logging 의존성 제거
configurations {
    all.collect { configuration ->
        configuration.exclude group: 'commons-logging', module: 'commons-logging'
        configuration.exclude group: 'log4j', module: 'log4j'
    }
}


[logback.xml]



	
		
			%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
		
	

	
		
	


[Foo.java]

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
	static final Logger logger = LoggerFactory.getLogger(Foo.class);

	public void doIt() {
		logger.debug("안녕 {}", "하세요.");
	}
}


SLF4J는 Apache commons-logging과 같이 SLF4J도 Facade 라이브러입니다.

그 말은 인터페이스만 있고, 해당 구현체는 별도의 라이브러리를 사용한다는 말입니다.


이에 대한 잇점은 시스템 전반적으로 사용되어진 로깅시스템의 교체가 수월해진다는 장점이 있습니다.

(사실 한번 적용된 로깅 시스템을 교체하는 경우는 특별한 경우가 아니고서야 별로 없긴 합니다.)


※ 하지만 SLF4J는 별도의 구현체 없이 단독으로도 사용가능하긴 합니다. ㅎ



SLF4J의 탄생 이유는 Aapche Commons-logging 의 비효율적인 문제들이라고 합니다.

여러가지가 있겠지만 잘 모르겠고..;; 개인적으로 편리한 기능 및 셈플 남겨봅니다.


logger.debug("param: " + param var); 


대부분 위와 같이 로그를 남길것이다.

하지만 운영의 DEBUG 하위 모드에서는 로그는 찍히지 않겠지만 로그 문자열에 대한 연산을 이루어져 그에 대한 비용이 발생한다고 합니다.

별거 아닌것 같지만 이에 대한 비용이 찝집하다네요 ㅎㅎ



if( logger.isDebugEnabled() ){

    logger.debug("param: " + param var);

}


그래서 대부분 위와 같이 특정 로그레벨일 때만 처리 되도록 하긴 하지만 소스양이 늘어나고 귀찮아지죠.

그에 따라 위와 같이 하지 않고 첫번째 방법으로만 하는 경우가 파다합니다.


SLF4J는 이러한 비용을 절약되도록 설계되었다고 합니다.



 logger.debug("param: {}", param var);


위와 같이 문자열을 연산하지 않고, 해당 함수 내부에서 상황에 따라 연산이 이루어지게 하는것 같습니다.


[build.gradle]

apply plugin: 'java'
apply plugin: 'eclipse'

sourceCompatibility = 1.6

repositories {
    mavenCentral()
}

// SLF4J 단독
List loggerSlf4j = [
    "org.slf4j:slf4j-api:1.7.5",
    "org.slf4j:slf4j-simple:1.7.5",
    "org.slf4j:jcl-over-slf4j:1.7.5"
]

// SLF4J + Log4j
List loggerSlf4jAndLog4j = [
    "org.slf4j:slf4j-log4j12:1.7.5",
    "org.slf4j:jcl-over-slf4j:1.7.5"
]

dependencies {
    //compile loggerSlf4j
    compile loggerSlf4jAndLog4j
}

// commons-logging 의존성 제거
configurations {
    all.collect { configuration ->
        configuration.exclude group: 'commons-logging', module: 'commons-logging'
    }
}


[log4j.xml]




	
		
			
		
	

	
		
		
	


[Foo.java]

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
	static final Logger logger = LoggerFactory.getLogger(Foo.class);

	public void doIt() {
		logger.debug("안녕 {}", "하세요.");
	}
}


예제 소스에서 볼수 있듯이, SLF4J단독으로 사용하든, Log4j를 구현체로 사용하든 소스상에 변화는 없습니다.


설정 중 한가지 눈여겨볼 수항은 "jcl-over-slf4j" 입니다.

"jcl-over-slf4j' 추가 목적은 기존 Springfrawork 와 같이 Apache commons-logging를 사용하는 라이브러리를 사용하고 있을 경우 소스상은 commons-logging이지만 실제 동작은 SLF4J쪽을 호출 하도록 하는 역할을 하도록 합니다.



SLF4J + Log4j 에 대한 예제만 작성하였지만 "http://www.slf4j.org/manual.html" 사이트를 참고하시면 아래와 같이 각 구현체 마다 사용해야 하는 라이브러리 정보가 있습니다.


slf4j-log4j12-1.7.5.jar
Binding for log4j version 1.2, a widely used logging framework. You also need to place log4j.jar on your class path.

slf4j-jdk14-1.7.5.jar
Binding for java.util.logging, also referred to as JDK 1.4 logging

slf4j-nop-1.7.5.jar
Binding for NOP, silently discarding all logging.

slf4j-simple-1.7.5.jar
Binding for Simple implementation, which outputs all events to System.err. Only messages of level INFO and higher are printed. This binding may be useful in the context of small applications.

slf4j-jcl-1.7.5.jar
Binding for Jakarta Commons Logging. This binding will delegate all SLF4J logging to JCL.

logback-classic-1.0.13.jar (requires logback-core-1.0.13.jar)
NATIVE IMPLEMENTATION There are also SLF4J bindings external to the SLF4J project, e.g. logback which implements SLF4J natively. Logback'sch.qos.logback.classic.Logger class is a direct implementation of SLF4J's org.slf4j.Logger interface. Thus, using SLF4J in conjunction with logback involves strictly zero memory and computational overhead.


var target:TraceTarget = new TraceTarget()
target.level = LogEventLevel.INFO;
target.includeDate = true;
target.includeCategory = true;
target.includeLevel = true;
Log.addTarget(target);
    
var logger:ILogger = Log.getLogger("mx.messaging.Channel");
logger.debug("here is some channel info {0} and {1}", 15.4, true);

+ Recent posts