개요
SLF4J(Simple Logging Facade for Java)는 이름의 Facade에서 알 수 있듯이
간단한 퍼사드 패턴을 로깅에 적용한 시스템으로 로깅 추상화 라이브러리이다.
퍼사드 패턴?
퍼사드 패턴(Facade Pattern)은 구조 패턴(Structural Pattern)의 한 종류로써, 복잡한 서브 클래스들의 공통적인 기능을 정의하는 상위 수준의 인터페이스를 제공하는 패턴이다.
java.util.logging, logback, log4j 등 다양한 로깅 프레임워크에 대해서 추상화 역할을 하는 인터페이스이다.
위의 로깅 프레임워크를 구현체, SLF4J를 인터페이스로 생각하면 이해하기 쉽다.
때문에 SLF4J는 반드시 구현체를 필요로 하게 된다.
SLF4J 기본 구성 요소
1. SLF4J API
- slf4j-api 라이브러리에서 제공되며, 로깅 인터페이스를 정의한다.
- Logger와 LoggerFactory를 통해 로그를 기록한다.
- 추상화 레이어로만 동작하며, 실제 로깅 구현체에 의존하지 않는다.
2. 로깅 구현체(Binding)
- 실제 로그 기록을 처리하는 구현체.
- SLF4J는 특정 로깅 라이브러리와의 연결을 위해 바인딩(binding)을 사용
- Ex) Logback, Log4j 2.x, java.util.logging 등이 구현체로 동작
3. SLF4J 바인딩
- slf4j-api와 로깅 구현체를 연결하는 역할
- 프로젝트에 포함된 바인딩 파일(Ex: slf4j-logback-classic.jar)을 통해 실행 시에 구현체가 결정된다.
SLF4J 동작 과정
- 로거 생성
- 바인딩 확인
- 로깅 구현체로 위임
- 로그 처리
위의 네 가지 과정을 통해 동작합니다.
하나하나 자세히 알아보겠습니다.
아 참, 먼저 의존성을 추가해줘야 합니다.
스프링 부트에서는 SLF4J, Logback을 기본 로깅 라이브러리 사용하기에 추가할 필요가 없지만,
스프링 부트가 없을경우 따로 의존관계를 추가해줘야 합니다.
build.gradle
plugins {
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
// SLF4J API 2.0.16 버전 사용
implementation 'org.slf4j:slf4j-api:2.0.16'
// Logback을 SLF4J 구현체로 사용
runtimeOnly 'ch.qos.logback:logback-classic:1.4.11'
runtimeOnly 'ch.qos.logback:logback-core:1.4.11' // logback-core 의존성도 필요
}
1. 로거 생성
애플리케이션은 LoggerFactory를 통해 로거 객체를 생성합니다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Logger log = LoggerFactory.getLogger(getClass());
2. 바인딩 확인
- 실행 시점에 SLF4J는 classpath에 로드된 바인딩 파일을 찾습니다.
- 특정 로깅 구현체의 바인딩이 존재할경우 해당 구현체를 로드합니다.
- Ex) slf4j-logback-classic.jar -> Logback 사용
3. 로깅 구현체로 위임
String name = "example";
log.trace("trace log={}", name);
log.debug("debug log={}", name);
log.info("info log={}", name);
log.warn("warn log={}", name);
log.error("error log={}", name);
log.info("info log=" + name);
/*
위처럼 +로 출력하는 방법은 설정에 따라 출력하지 않으려 해도 + 로직이 수행되어
{}을 사용하는 방법보다 속도가 10배 느리다고 한다.
꼭 {}를 사용하도록 하자!
*/
애플리케이션이 위와 같이 호출한 로깅 메서드(info, warn 등)는
SLF4J API를 통해 로깅 구현체(Logback 등)로 전달됩니다.
4. 로그 처리
로깅 구현체(Logback)은 설정 파일(Ex: Logback의 logback.xml, 스프링부트에선 application.properties)을 읽고,
메시지의 포맷, 레벨, 출력 대상 등을 결정합니다.
logback.xml - src/main/resources 폴더에 배치
<configuration>
<!-- 콘솔 출력 설정 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 로그 레벨 설정 -->
<root level="debug">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Logback 설정 참조 순서
- classpath에서 logback-test.xml 파일 탐색
- 못 찾았으면, logback.groovy 파일 탐색
- 못 찾았으면, logback.xml 파일 탐색
- 못 찾았으면, JDK 1.6의 service-provider loading facility (Service Loader)에 의해 com.qos.logback.classic.spi.Configurator 인터페이스의 구현체를 찾음.
- 위 과정을 모두 실패하면, Logback은 콘솔에 출력하는 BasicConfigurator으로 설정(기본 에러 출력은 info)
application.properties
#전체 로그 레벨 설정(기본값 info)
logging.level.root=info
로깅의 장점?
실무에서는 무조건 시스템 콘솔로 출력하는 대신 로그를 사용한다.
- 쓰레드 정보, 클래스 이름같은 부가 정보를 함께 볼 수 있고, 출력 모양을 조정할 수 있다.
- 로그 레벨에 따라 개발 서버에서는 모든 로그를 출력하고, 운영 서버에서는 출력하지 않는 등 로그를 상황에 맞게 조절할 수 있다.
- 시스템 콘솔에만 출력하는 것이 아니라, 파일이나 네트워크 등 로그를 별도의 위치에 남길 수 있다. 특히 파일로 남길 경우 일별, 특정 용량 별로 로그를 분할할 수 있다.
- 성능도 System.out보다 뛰어나다. (내부 버퍼링, 멀티 쓰레드 등)
Reference
https://livenow14.tistory.com/63
https://www.slf4j.org/manual.html
https://www.slf4j.org/faq.html