SLF4J 이해하기 3탄 - SLF4J 그리고 Logback

Shinny·2022년 7월 22일
3

Logging

목록 보기
3/3

이전 글에서 갑자기 블로그에 한번도 올린 적 없던 디자인 패턴을 다루었었죠? 그건 바로 제가 SLF4J 를 공부하던 도중 Facade에 대한 기본적인 이해가 우선적으로 필요하다고 생각했기 때문이에요.

SLF4J

SLF4J는 Simple Logging Facade for Java 라는 이름에서부터 알 수 있듯이, Logback, Log4j2와 같은 Logging Framework의 추상화 역할을 해요. 추상화 로깅 라이브러리이기 때문에 단독으로는 사용할 수 없어요. 배포 시에 개발자가 로깅 프레임워크를 선택해서 연결해줘야 비로소 사용할 수가 있죠.

제일 처음에, Java에서 Log를 남기기 위해서는 slf4j-api dependency를 추가해줘야해요.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.25</version>
</dependency>

그리고 아래의 코드로 실행을 해 보았더니…!

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

public class App {
    public static void main(String[] args) {
				Logger logger = LoggerFactory.getLogger(App.class);
        logger.info("INFO {}", ": Logging Started");
    }
}

이런… 에러가 뜨고 말았네요. 앞서 말했든 SLF4J는 추상체이기 때문에, 단독으로 사용할 수가 없어요.

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

이럴 때 SLF4J 공식문서(링크)에서는 이렇게 말하고 있어요.

This warning is printed because no slf4j binding could be found on your class path.

즉, 이 경고는 SLF4J 바인딩이 없기 때문에 생기는 거라고 말해주고 있죠?
그래서 저는 아래와 같은 dependency를 또 추가해주었어요.

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.25</version>
</dependency>

그리고 나서 다시 실행하니, 두둥!

[main] INFO com.example.java8.App - INFO : Logging Started

정상적인 로그가 찍히고 있네요.

그리고 Logback 프레임워크를 사용하기 위해서 binding은 logback-classic을 쓸 수가 있어요. 또한, logback-classic dependency를 추가하면 자동으로 slf4j-api, logback-core가 추가돼요. 하지만 명시적으로 artifactId를 지정해주는 것이 더 좋기 때문에, 아래처럼 exclude 한 다음에 따로 dependency를 추가할 수도 있어요.

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
	<version>1.2.6</version>
</dependency>

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
	<version>1.2.6</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

잠깐, 근데 왜 SLF4J를 사용해야 할까요?

예를 들어, log4j를 걷어내고 logback으로 교체하는 업무가 주어졌다고 가정해봐요. (Java 버전이 올라가면서 log4j가 문제를 일으키는 상황들이 있다고 해요.)

그럼, 우리는 우선 maven이나 gradle에서 log4j의 dependency를 exclude하고 다시 logback을 추가를 합니다. 그리고 log4j로 import 된 것을 logback으로 바꾸고… 수작업으로 처리해줘야 할 일이 상당히 많아질 수 있어요. 여기서 SLF4J를 사용한다면 이런 일을 방지할 수가 있어요. 왜냐하면 SLF4J는 Java의 로깅 모듈들의 추상체이기 때문이에요.

(Bad👎) 하나의 라이브러리에 의존적인 코드 예시

import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggerFactory;

Logger log = Logger.getLogger(this.getClass());
log.warn("=====NO, log4j=====")

(Good👍) 추상체에 의존적인 코드 예시 - framework 손쉽게 변경 가능

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

Logger logger = LoggerFactory.getLogger(App.class);
logger.info("=====YES, slf4j=====");

또, 대부분의 자바 로깅 모듈은 slf4j의 브릿지를 이미 제공해주고 있어서 slf4j와 다른 프레임워크를 연결하기 위해 추가로 구현 작업이 필요없습니다. 이미 만들어진 API를 찾아 그냥 넣기주기만 하면 됩니다. logback을 쓰고 싶으면 slf4j-api를, log4j2를 쓰고 싶다면 log4j-slf4j-impl과 log4j-api를 추가하면 됩니다. 즉 slf4j를 사용함으로써 하나의 라이브러리에 종속적일 필요가 없게 되는 거죠.

SLF4J의 동작 과정

  1. SLF4J Bridging Modules
    1. 다른 로깅 API로의 Logger 호출을 SLF4J 인터페이스로 리다이렉트하여 SLF4J API가 대신 처리할 수 있도록 일종의 어댑터 역할을 하는 라이브러리
    2. log4j-over-slf4j, jcl-over-slf4j, jul-to-slf4j
  2. SLF4J API(인터페이스)
    1. 로깅에 대한 추상 레이어를 제공합니다. 즉 추상 메서드를 제공해요.
    2. 하지만 위에서 봤듯이 이 dependency만 추가하면 binding 이 없다는 에러를 만나게 돼요. 추상 클래스이기 때문에 단독적으로 사용할 수가 없는 것이죠.
    3. slf4j-api
  3. SLF4J Binding
    1. SLF4J 인터페이스를 Logging Framework와 연결하는 어댑터 역할을 하는 라이브러리
    2. SLF4J API를 구현한 클래스에서 Binding으로 연결될 Logger의 API를 호출해요. 하나에 API에는 하나의 Binding만을 두어야 해요.
    3. logback: logback-classic
  4. Logging Framework
    1. logback: logback-core

slf4j는 컴파일 시 Logging Framework를 선택할 수 있다는 장점이 있어요.
만일 이를 변경하고자 한다면, 위에서 Logging Framework 와 Binding을 바꾸면 돼요.

Logback

Logback은 Log4j의 improved 버전이에요. 심지어 Log4j를 만든 개발자가 직접 만들었죠.

Logback을 Spring에서 사용하기 위해서는 resources 디렉토리 안에 logback-spring.xml 파일을 만들어서 설정을 해주면 돼요.

<configuration>
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</Pattern>
    </layout>
  </appender>

  <appender name="fout" class="ch.qos.logback.core.FileAppender">
    <file>baeldung.log</file>
    <append>false</append>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %m%n</pattern>
    </encoder>
  </appender>

  <logger name="com.baeldung.log4j" level="TRACE"/>

  <root level="INFO">
    <appender-ref ref="stdout" />
    <appender-ref ref="fout" />
  </root>
</configuration>

Spring Boot에는 기본적으로 logback이 포함되어 있어요. 그래서 Application을 실행시킬 때 IDE console 창을 보면 LogbackApplication이 시작되었다는 문구를 확인할 수가 있는데요.

Spring Boot에서는 logback.xml로 설정을 하면 스프링 부트 설정 전에 logback 설정이 되므로 제어할 수가 없게 돼요. 그래서 Spring boot에서는 resources/logback-spring.xml 에서 설정을 하거나 혹은 application.properties를 통해 logback을 제어할 수가 있어요.
Spring 혹은 일반 java 프로그램의 경우 그냥 logback.xml을 resources 경로 안에 생성해주면 돼요.

Logback Appenders

그리고 만일, "개발 환경에서는 로그를 Debug 레벨까지 모두 Console에 찍어주고, 배포 환경에서는 파일로 따로 관리하도록 시스템을 구축해주세요!" 라는 요구사항이 들어왔다면 이 부분을 잘 읽어봐야할 것같아요. 자세한 내용은 공식문서(링크)를 참고해주세요!

Reference

https://www.baeldung.com/java-logging-intro
https://www.baeldung.com/slf4j-with-log4j2-logback
https://logback.qos.ch/
https://livenow14.tistory.com/63
https://inyl.github.io/programming/2017/05/05/slf4j.html
https://velog.io/@stella6767/logback-spring.xml-%EC%84%A4%EC%A0%95%EB%B0%A9%EB%B2%95
https://dadadamarine.github.io/java/spring/2019/05/01/spring-logging-xml.html

profile
비즈니스 성장을 함께 고민하는 개발자가 되고 싶습니다.

0개의 댓글