스프링을 사용하면 기본적으로 JCL(자카르타 커먼스 로깅)을 사용하게 되는데 JCL이 실제 로거를 선택하는 시점이 런타임이라 클래스로더 문제라던가 런타임시 오버헤드가 생길 수 있는데 이것을 개선한 구현체 SLF4J로 갈아타면 그러너 문제 걱정을 덜 수 있겠습니다. 보너스로 문자열 연결로 발생하는 오버 헤드도 개선할 수 있으며 귀찮은 if문 추가하는 코딩에서 벗어날 수 있는 문법? API?를 제공해줍니다. 그래서인지 스프링도 3.0부터는 본격적으로 spring-core가 의존하는 JCL을 SLF4J로 교체하고 사용하는 예제 및 레퍼런스 설명을 보여주고 있습니다..

갈아타는 방법은 다음과 같습니다.

1. 스프링에서 참조하는 JCL 라이브러리를 빼버리기.
2. JCL-over-SLF4J 추가하기.
3. SLF4J API 추가.
4. SLF4J-log4j 추가.
5. log4j 추가.

이전에 스프링소스 블로그와 레퍼런스에 올라온 방법은 이걸 그대로 메이븐 의존성으로 옮겨적고 있지요.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

1번은 단순히 JCL 라이브러리에 의존할 생각이 없기 때문에 빼버리는 것이고, 그때 JCL에 의존하는 클래스들이 깨질 텐데 그것을 JCL-over-SLF4J를 이용해서 겉은 JCL 같지만 내부에서는 SLF4J API를 호출하도록 일종의 어댑터 나 다리 역할을 해주는 라이브러리를 추가하고, 인터페이스 격인 3. SLF4J API를 추가한 뒤 실제 사용할 로거로 SL4J를 구현한 4. SLF4J-Log4J 라이브러리를 추가합니다. 마지막으로 최종적으로 사용할 로거 5. Log4J를 추가한 것입니다. 이때 Log4J가 불필요하게 참조하는 jmx, mail, jms 등의 라이브러리를 제외시켜줍니다.

만약에 Log4J가 아니라 다른 로거를 사용할 거라면 4번과 5번을 교체하면 될테고 JCL 뿐 아니라 log4j에 대한 직접 호출도 slf4j로 오게 하려면 2번 대신 log4j-over-slf4j을 추가하면 되겠습니다.

굉장히 장황니다. @_@;; 하지만 뭐.. 좋다는데.. 갈아타긴 해야겠죠.

출처: SLF4J in 10 slides, by Ceki Gülcü

위와 같은 설정은 스프링 3.0 예제와 레퍼런스에서 사용하고 있습니다. 정말 장황합니다. 일단 여기서 1, 2번은 필수다 하지만 레퍼런스와 블로그 글에도 나와있듯이 그 이하 3, 4, 5는 Logback이라는 SLF4J API 구현체로 한방 설정이 가능합니다.

logback은 크게 세 가지 모듈로 나뉘는데 그 중에 SLF4J API 구현체인 classic 모듈이 있고, Log4J를 개선한 core 모듈이 있습니다. logback 모듈을 가져오면 위에서처럼 log4j가 끌고오는 부가적인 라이브러리도 없고 깔끔하게 log4j API를 사용할 수 있습니다.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>com.springsource.ch.qos.logback.classic</artifactId>
            <version>0.9.9</version>
        </dependency>

따라서 굳이 Log4J가 아닌 다른 로거로 갈아탈 계획이 없다면 이렇게만 설정해도 되겠습니다. 로깅 설정만 거의 1/3로 줄어듭니다.

사용법은

1. SLF4J API를 이용해서 로거를 만들고..

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

Logger log = LoggerFactory.getLogger(AccountJsonBindingTests.class);

2. 다음과 같이 로깅하면 됩니다.

log.info("name is {}", name);

+를 사용해서 문자열 연산을 할 필요도 없고, if문을 줘서 문자열 연산을 막을 필요도 없습니다.

3. log4j 설정은 프로퍼티 파일이나 XML로 할 수 있는데 스프링 3.0 예제는 보통 XML을 사용하더군요. src와 test 소스 폴더 하위의 클래스패스 루트에 각각 다음과 같은 log4j.xml 파일을 둡니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p: %c - %m%n" />
        </layout>
    </appender>

    <!-- 3rdparty Loggers -->
    <logger name="org.springframework.core">
        <level value="info" />
    </logger>

    <logger name="org.springframework.beans">
        <level value="info" />
    </logger>
   
    <logger name="org.springframework.context">
        <level value="info" />
    </logger>

    <logger name="org.springframework.web">
        <level value="info" />
    </logger>

    <!-- Root Logger -->
    <root>
        <priority value="warn" />
        <appender-ref ref="console" />
    </root>
   
</log4j:configuration>

src와 test 간의 차이는 마지막 부분의 <root> 안의 <priority>가 src에서는 warn이고 test에서는 info라는 것 뿐이 없습니다. 이 설정에서는 Log4J를 사용하고 있는데.. logback API를 이용해서 설정해도 당근 잘 동작합니다.