자바의 로깅 프레임워크
이슈
- 특정 라이브러리의 로그가 찍히지 않아 트러블 슈팅을 하던 중,
- 애플리케이션에 온갖 로깅 관련 의존성들(slf4j, reload4j, log4j2 등등..)이 추가되어 있는 것을 보고, 각 의존성들이 정확히 어떤 역할을 하고 왜 필요한지를 알기 위해 자바의 로깅 프레임워크에 대해 자세히 살펴보기 시작했다.
추상화 레벨에 따른 대분류
우선, log4j2니, logback이니 slf4j니 종류가 많으니, 각각의 추상화 레벨을 구분해서 살펴볼 필요가 있다.
여기서는 대분류로, 추상화 레이어와 구현체 레이어로 구분해서 다룰 것이다.
- 추상화 레이어 : 애플리케이션에서 바라보는 레이어로, 인터페이스에 해당하고,
- 구현체 레이어 : 추상화 레이어를 구현한 구현체(혹은 백엔드). 실제 로깅 동작을 다룬다.
실제로 자바 언어 레벨에서도 이 둘의 관계는 interface와 이를 implement한 class의 관계로 구현된다.
(둘 사이에 호환을 위해 어댑터 레이어가 있는 경우가 있으나, interface를 구현한 class라는 관계는 변하지 않는다.)
아래 이미지는 SLF4J 공식 레퍼런스에서 가져온 구성도이다. 구체적인 내용은 아래에서 자세히 살펴보더라도, 아래 이미지를 통해 레이어의 구성만이라도 먼저 파악하면 이해가 빠르다.
여기서 레이어의 구성이라함은, application은 추상화 레이어(SLF4J-API)를 바라보고, 추상화 레이어는 구현체 레이어(로깅 백엔드)를 바라보는 구성을 의미한다.
(그리고 아래에서 각 로깅 컴포넌트들을 다룰때, 이름만 보면 상당히 혼란스러울 수 있기 때문에 group id와 artifact id 도 함께 명시할 예정이다.)
배경
살펴보기에 앞서, 왜 이렇게 자바의 로깅 라이브러리들이 중구난방으로 발생하게 된 건지 살펴보면 각자의 존재 이유를 이해하는데 도움이 된다.
우선, SLF4J 가 등장하기 이전의 자바 생태계에서는 통일된 인터페이스 없이 log4j, jul, commons-logging 등 다양한 로깅 라이브러리가 혼재되어 사용되었다.
당연하게도, 이렇게 파편화된 의존성들은 전혀 호환이 되지 않았고, 복잡도를 증가시키는 문제가 있었다.
이러한 문제를 해결하기 위해, log4j를 만든 개발자 Ceki Gülcü(이하 Ceki) 로깅을 위한 통합 퍼사드인 SLF4J 를 만들게 되었다.
(Ceki 선생님은 자바 로깅 프레임워크를 논의할때 빼놓을 수 없는 인물이고, 아래에서도 자주 언급될 예정이다. 참고로, 후술할 컴포넌트 중 group id ch.qos
에 포함되는 컴포넌트들은 모두 Ceki 선생님이 만들고 관리하고 있다고 보면 된다.)
그리고 이런 퍼사드 레이어와 별개로 각자 구현체들이 만들어지게 되었고, 그 구현체들과 퍼사드를 이어주는 어댑터들이 만들어지게 되었다.
추상화 레이어
애플리케이션에서 바라보는 레이어이다.
실제로 애플리케이션(혹은 라이브러리) 개발자는 추상화 레이어의 인터페이스 기준으로 개발하는 것이 일반적이다.
추상화 레이어 자체는 로깅의 구체적인 동작 방식을 포함하지 않기 때문에,
추상화 레이어만으로 컴파일은 가능하나, 추상화 레이어만으로는 런타임에 로깅을 수행할 수 없다.
SLF4J
org.slf4j.slf4j-api
- SLF4J는 Simple Logging Facade for Java를 의미하며, 앞서 언급했듯이, Ceki 선생님이 파편화된 자바 로깅 정국을 통합하기 위해 만든 로깅 퍼사드이다.
- 사실상 자바 로깅의 표준으로 자리 잡았다.
- Spring 프레임워크 5.x 이후 버전 및 Spring boot의 디폴트 로깅 퍼사드로 채택되었다.
- lombok의
@SLF4J
어노테이션을 통해 사용할 수 있으며, Vanilla java로는 아래와 같이 사용한다.public class LogExampleOther { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class); public static void main(String... args) { log.error("Something else is wrong here"); } }
- 공식 레퍼런스 : SLF4J Manual
Apache Commons Logging
commons-logging:commons-logging
- Apache commons 프로젝트 중 로깅과 관련된 컴포넌트의 모음으로 시작됐다.
- JCL(Jakarta Commons Logging)이라고도 불린다.
- Spring 프레임워크 4.x 버전까지 디폴트 로깅 퍼사드로 채택되었다. 앞서 언급했듯, 이후 버전에서 SLF4J에게 자리를 내주었다.
- 그러나 Spring 프레임워크 내부적으로는 commons-logging을 여전히 사용한다.
- lombok의
@CommonsLog
어노테이션을 통해 사용할 수 있으며, Vanilla java로는 아래와 같이 사용한다.public class LogExampleCategory { private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog"); public static void main(String... args) { log.error("Calling the 'CounterLog' with a message"); } }
- 공식 레퍼런스 : Overview – Apache Commons Logging
구현체 레이어
실제 로깅의 동작을 정의하는 로직을 담고 있는 레이어이다.
추상화 레이어를 직접 구현하는 경우도 있고, 어댑터를 통해 연결되는 경우도 있다.
위의 추상화 레이어 라이브러리(SLF4J, commons-logging)는 각자 방식에 따라 적절한 구현체를 선택한다.
Log4j
log4j.log4j
- Log4j 의 1.x 버전
- Ceki 가 만든 로깅 라이브러리로, 자바 초창기부터 사용되었다.
- 2015년 8월자로 개발이 중단되었다.
On August 5, 2015 the Logging Services Project Management Committee announced that Log4j 1.x had reached end of life. … Users of Log4j 1 are recommended to upgrade to Apache Log4j 2.
- 후술할 Logback, Log4j2의 전신이 되었으나, API의 backward compatibility는 보장되지 않는다.
- log4j-12라고도 불려서 후술할 log4j2와 아주 혼동의 여지가 많으나,
- log4j-12는 log4j 1.2.x 버전을 의미하고
- log4j2는 log4j 2.x 버전을 의미한다.
- 공식 레퍼런스 : Apache log4j 1.2 -
Reload4j
ch.qos.reload4j.reload4j
- 앞서 언급한 log4j 1.x 버전의 개발이 중단되면서, log4j 1.x 버전의 보안 취약점 이슈 대응만을 위해 등장한 라이브러리
Initiated by Ceki Gülcü, the original author of Apache log4j 1.x, the reload4j project is a fork of Apache log4j version 1.2.17 with the goal of fixing pressing security issues
- log4j 1.2.17 버전의 fork
- log4j 1.x 버전을 사용하고 있다면, 보안 취약점 대응을 위해서는 reload4j로 의존성을 업데이트하는 것을 권장하며, log4j 1.x 버전과 binary compatibility를 보장한다.
- groupId가 바뀐 것을 보면 눈치챌 수 있듯이, log4j 1.x 버전의 창시자인 Ceki가 관리한다.
- 공식 레퍼런스 : reload4j
Logback
ch.qos.logback.logback-classic
- log4j의 후속작으로서, log4j와 SLF4J의 창시자인 Ceki 가 만들었다.
- SLF4J를 만들면서 그 구현체로서 Logback을 같이 만들었다.
- SLF4J의 구현체로서 만들어졌기 때문에 별도 어댑터 없이 SLF4J API를 다이렉트로 구현한다. (공식 레퍼런스에서는 이를 native implementation이라고 하는 듯 하다.)
- 실제로 Logback의 Logger의 구현을 보면 아래와 같다.
- Spring boot의 디폴트 구현체로 채택되었다.
- 공식 레퍼런스 : Logback Home
Log4j2
org.apache.logging.log4j:log4j-core
- log4j의 후속작으로서, 아파치 재단에서 만들었다.
- 이름만 log4j2 이지, log4j와 코드베이스도 전혀 다르고, 하위 호환도 되지 않는다.
- log4j 1.x 버전의 아티팩트명은
log4j.log4j
로, log4j2와 전혀 다르다. - 호환을 위해서는 별도 어댑터 라이브러리(
log4j-1.2-api
)가 필요하다.
- log4j 1.x 버전의 아티팩트명은
- 그리고 log4j2의 버전은 2점대부터 시작한다.
- 이름을 log4j2로 만든 건 log4j의 계승의 의미로 붙혔다고 하는데, 개인적으로는 이러한 이름 때문에 처음보는 입장에서는 헷갈렸다.
- Logback과의 차이점은 SLF4J를 native로 구현하지 않는다.
- 따라서 SLF4J 인터페이스 호환을 위해서는 후술할 어댑터(
log4j-slf4j-impl
혹은log4j-slf4j2-impl
)가 필요하다. - 이는 SLF4J로 대표되는 Ceki 진영과 독립적인 노선을 타기 위한 아파치 재단의 의도로 보인다.
- 따라서 SLF4J 인터페이스 호환을 위해서는 후술할 어댑터(
- SLF4J를 직접 구현하지 않기 때문에, SLF4J 의존성 없이 Log4j2 독립적으로 사용이 가능하다.
- 공식 레퍼런스 : Log4j – Apache Log4j 2 - Apache Log4j 2
JUL
java.util.logging
- java 1.4부터 jdk 레벨에서 제공하는 로깅 라이브러리
- SLF4J가 있기 전부터 있던 로깅 라이브러리로, 현재는 잘 사용하지 않는 듯 하다.
- Spring 4.x 버전까지 디폴트 구현체로서 사용되었다.
- 공식 레퍼런스 : java.util.logging (Java Platform SE 8 )
그 외
org.slf4j.slf4j-simple
- SLF4J의 native 구현체로, 모든 로그를 stderr로 출력하는 로거
org.slf4j.slf4j-nop
- SLF4J의 native 구현체로, 아무것도 하지 않는 로거. No operation.
어댑터 레이어
위에서 언급한 로깅 컴포넌트들은 서로 호환이 되지 않는 경우가 많다.
추상화-추상화 레이어 혹은 구현체-구현체 레이어 간에는 당연히 호환이 안되지만,
추상화-구현체 레이어 간에도 native로는 호환이 안되는 경우가 있다. (예를 들어, SLF4J 와 Log4j2)
따라서 이러한 호환성을 위해서 어댑터 레이어의 라이브러리들이 별도로 존재한다.
그리고 그 라이브러리의 아티팩트명은 보통 연결하고자 하는 두 컴포넌트의 이름을 포함한다.
그러므로, 두 개 컴포넌트 이름을 포함하고 있는 jar가 있다면, 어댑터 레이어겠거니 생각하면 된다.
아래는 대표적인 어댑터를 나열하였다.
추상화-추상화 레이어간 어댑터
org.slf4j.jcl-over-slf4j
: application -> commons-logging -> adapter -> SLF4J- 사용 예는 commons-logging으로 작성된 애플리케이션을 코드 수정없이 SLF4J 와 호환되도록 만들어준다.
추상화-구현체 레이어간 어댑터
org.apache.logging.log4j.log4j-slf4j-impl
: application -> SLF4J(1.7.x 이하) -> adapter -> log4j2 형태로 연결한다.- log4j2의 SLF4J 바인딩
- 이름엔 log4j2가 없지만, log4j가 아니라 log4j2이다.
org.apache.logging.log4j.log4j-slf4j18-impl
: SLF4J 1.8 이상org.apache.logging.log4j.log4j-slf4j2-impl
: SLF4J 2.0
- log4j2의 SLF4J 바인딩
org.apache.logging.log4j.log4j-jcl
: application -> commons-logging -> adapter -> log4j2- log4j2의 commons-logging 바인딩
org.slf4j.slf4j-reload4j
: application -> SLF4J -> adapter -> log4j 1.x- log4j 1.x 버전의 SLF4J 바인딩
- 기존에는
org.slf4j.slf4j-log4j12
였으나,slf4j-reload4j
로의 relocation을 권장한다.
org.slf4j.jul-to-slf4j
: application -> jul -> adapter -> slf4j- jul (구현체) 로 작성된 애플리케이션을 SLF4J(추상화)로 연결
- 위 세개의 예시는 추상화 레이어에 구현체 레이어를 바인딩하는 어댑터인 반면, 이 라이브러리의 방향은 반대이다.
- JUL로 작성된 레거시 코드의 SLF4J 호환 용도로 사용
- 참고 : Log4j Bridge
- jul (구현체) 로 작성된 애플리케이션을 SLF4J(추상화)로 연결
구현체-구현체 레이어간 어댑터
org.apache.logging.log4j.log4j-1.2-api
: application -> log4j -> adapter -> log4j2- 호환되지 않는 log4j와 log4j2를 연결한다.
References
- Apache Commons – Apache Commons
- SLF4J Manual
- reload4j
- Log4j Bridge
- @Log (and friends)
- Apache log4j 1.2 -
- Log4j – Apache Log4j 2 - Apache Log4j 2
- Logback Home
- GitHub - qos-ch/logback: The reliable, generic, fast and flexible logging framework for Java.
- Logging :: Spring Boot
- java 의 로깅 이란 (JCL, SLF4J, log4j, logback)
- 코끼리를 냉장고에 넣는 방법 :: [Logback] 로그백(logback) 스프링(Spring)에서 사용 하는 방법
Leave a comment