프로젝트에 로그를 수집하여 모니터링할 수 있는 기능을 추가하려고 Spring Boot의 Logging문서를 보게 되었다. 원래는 Logback-spring.xml만 잘 설정해서 로그 기능만 추가하려는 생각을 했다가 문득 왜 Logback을 사용하고 Commons Logging은 뭐지?라는 궁금증이 들었고 그 내용을 한번 정리해 보자고 마음먹게 되었다.
1. Commons Logging(JCL) 이란?
Spring Boot로 프로젝트를 시작할 때, spring-starter-web 의존성을 추가하고 애플리케이션 로그를 출력하는 코드를 작성하면 아래 그림과 같이 commons.logging과 관련된 import 코드가 추가되는 것을 볼 수 있다.

위 코드를 실행시켜 Log 클래스를 출력해보면 아래와 같은 결과가 나온다.

우리가 Spring Boot로 코드를 작성할 때, Service를 인터페이스로 만들고 그 인터페이스를 구현한 ServiceImpl을 Controller에 런타임에 의존성을 주입해 주는 것과 비슷한 게 바로 Commons Logging이다.

즉, 여러 로그 파사드(Log4j-api, SLF4J-api 등)가 존재할 때 JCL(Commons Logging)은 자동 탐색을 통해 적절한 파사드를 선택하고, 파사드는 실제 로그 구현체에 로그 출력을 위임한다. 이를 통해 사용자는 내부의 복잡한 로그 출력 로직이나 구조를 알 필요 없이 Log.info(...)와 같은 단순한 메서드 호출만으로 로그를 남길 수 있다. 이러한 설계를 보면 코드의 확장성과 캡슐화를 고려하여 만들어졌음을 알 수 있다.
실제로 Commons Logging의 공식문서를 보면 아래의 관점에서 만들어졌다는 것을 알 수 있다.
세상에는 다양한 로깅 구현체들이 존재하며, 특정 라이브러리가 자신이 속한 전체 애플리케이션에 특정 로깅 구현체 사용을 강제할 수는 없다.
Commons-Logging 패키지는 이러한 여러 로깅 구현체 사이에서 동작하는 초경량 브리지(bridge) 역할을 한다. 즉, 라이브러리가 commons-logging API를 사용하면 런타임 시 어떤 로깅 구현체와도 함께 동작할 수 있다. Commons-Logging은 이미 여러 인기 있는 로깅 구현체를 지원하고 있으며, 다른 구현체와 연동하기 위한 어댑터를 작성하는 것도 비교적 단순하다.
Overview – Apache Commons Logging
The Logging Component When writing a library it is very useful to log information. However there are many logging implementations out there, and a library cannot impose the use of a particular one on the overall application that the library is a part of. T
commons.apache.org
하지만, Spring은 Apache의 Commons Logging을 그대로 쓰지 않고 직접 경령화해 구현한 Spring JCL을 사용한다.
spring-framework/spring-jcl/src/main/java/org/apache/commons/logging at 6.2.x · spring-projects/spring-framework
Spring Framework. Contribute to spring-projects/spring-framework development by creating an account on GitHub.
github.com
그 이유는 뭘까? 이유를 파악하기 위해 Commons Logging과 인기있는 로깅 구현체를 Java 프로젝트에서 연결시키는 과정을 실습해 보았다.
2. 브리지(Bridge), Provider 자동 탐색 실습
그전에 브리지라는 개념과 Provider 자동 탐색에 대한 개념을 잡고 가자. 브리지는 A라는 로깅 API로 들어온 호출을 B라는 다른 API로 중계해 주는 변환 어댑터이다. 즉, 겉으로 보이는 API는 그대로 쓰고 내부의 처리는 다른 API에 위임하는 것이다.
Provider 자동 탐색은 런타임에 클래스패스에 어떤 로깅 구현체가 존재하는지 살펴보고 실제 요청을 보낼 로깅 구현체를 자동으로 선택하는 것을 말한다.
Commons Logging 1.2 버전까지는 지원하는 로깅 구현체가 (Log4j 1.x, Avalon, Lumberjack) 같은 과거의 구현체만을 지원하고 있었고 SLF4J나 Log4j 2 같은 경우 JCL이 자동으로 탐색해 선택하는 대상이 아니었다. 따라서 나온 라이브러리들이 바로 `jcl-over-slf4j`, `log4j-jcl`다.
`jcl-over-slf4j`의 경우 jcl 자체를 재구현해 slf4j 구현체를 사용할 수 있도록 만든 대체제이고 log4j-jcl 같은 경우도 log4j2를 지원하기 위해 만들어진 브리지다.
이 내용을 바탕으로 Gradle 기반의 자바 프로젝트에서 JCL과 구현체를 연결하는 실습을 진행해 보았습니다.
실습 환경과 코드는 다음과 같습니다.
`build.gradles.kts`
dependencies {
implementation("commons-logging:commons-logging:1.2")
implementation("org.slf4j:jcl-over-slf4j:2.0.12")
implementation("org.slf4j:slf4j-api:2.0.12")
runtimeOnly("ch.qos.logback:logback-classic:1.4.14")
// implementation("org.apache.logging.log4j:log4j-jcl:2.23.1")
// implementation("org.apache.logging.log4j:log4j-api:2.23.1")
// runtimeOnly("org.apache.logging.log4j:log4j-core:2.23.1")
}
public class App {
private static final Log log = LogFactory.getLog(App.class);
public static void main(String[] args) {
log.trace("trace message");
log.debug("debug message");
log.info("info message");
log.warn("warn message");
log.error("error message");
log.error("[WHO?] " + log.getClass().getName());
}
}
가장 먼저 Commos Logging만 추가하고 실행해 보았을 때는 JUL(Java 기본 로거)이 구현체로 선택되는 것을 확인할 수 있었다.

다음은, Log4j2-api와 log4j2-core만 추가하였고 이때도 역시 JUL이 선택되었고 공식문서의 설명대로 실제 Log4j2를 인식하지 못하는 것을 확인할 수 있었다.

log4j2-jcl을 추가하고 다시 한번 실행시켰을 때는 Log4j2 로거를 이용하는 것을 확인했다.

다음은 SL4FJ로 Log4j2와 비슷하게 실습을 진행했고 결과도 예상하는 대로 출력되었다.


이 과정을 통해서 Spring-jcl이 만들어진 이유를 판단해 보았을 때, Spring은 인기 있는 로거를 도입시키고 확장가능하게 제공하고 싶었지만 JCL의 현대적 로그 구현체를 지원하지 않았고 그에따라 직접 경량 구현을 진행한게 아닌가?라고 생각이 들었다.
이것 외에도 JCL의 복잡한 자동 탐색 로직 때문에 Spring은 JCL을 Spring-jcl로 재구현하기로 결정한 것 같다.
3. Commons Logging 1.3의 등장
Apache Logging Services
Upgrade to Apache Commons Logging 1.3.0 Instructions to upgrade application to Apache Commons Logging 1.3.0. 02 Dec 2023 Apache Commons Logging Apache Commons Logging (JCL) is one of the oldest Java logging API available. Released for the first time in 200
logging.apache.org
앞에서 계속 현대적 로깅 구현체를 지원하지 않는다고 말했지만, 2023년 Commons Logging이 드디어 인기있는 로깅 구현체에 대해 자동탐색을 지원하도록 변경되었다. 탐색 로직의 경우에도 Spring-jcl과 비슷하게 Log4j → Slf4j → JUL 순으로 자동 포워딩 지원으로 변경되었다.
그에 따라, Spring Framework 7 버전부터는 Spring-jcl이 삭제되었고 실제로 위 Java 프로젝트에서 Commons Logging 버전 1.3을 사용하게 되면 브리지 없이 SLF4J 구현체와 Log4j2 구현체에 알아서 연결된다.
실제 SpringFramework의 main 브랜치에는 이제 더 이상 spring-jcl 모듈을 찾아볼 수 없다. (6.2.x 에서는 아직 볼 수 있다.)
GitHub - spring-projects/spring-framework: Spring Framework
Spring Framework. Contribute to spring-projects/spring-framework development by creating an account on GitHub.
github.com
4. 하지만.. 내가 사용하는 Spring 버전은 6인걸?
Spring Framework 6까지는 spring-jcl로 동작하기 때문에 spring-jcl에 대해서도 공부해 보았다.
Logging :: Spring Framework
Spring’s Commons Logging variant is only meant to be used for infrastructure logging purposes in the core framework and in extensions. For logging needs within application code, prefer direct use of Log4j 2.x, SLF4J, or JUL.
docs.spring.io
공식문서의 설명에 따르면 Spring-jcl의 자동 탐색 순서는 다음과 같다.
- 클래스패스에 Log4j 2.x API와 SLF4J 1.7 API가 있는지 확인한다
- 둘 중 먼저 발견되는 것을 로깅 구현으로 사용한다.
a. Log4j 2.x API가 있으면 -> Log4j2 경로로 위임
b. 아니면 SLF4J API가 있으면 -> SLF4J 경로로 위임
c. 둘 다 없으면 자바 기본 로깅 JUL 사용
따라서, Spring Framework를 사용할 때 로그 기능을 사용하려면 위 내용을 참고해서 의존성을 추가해줘야 한다.
그런데, 우리가 직접 추가한 적이 있나?라는 생각을 해보았을 때 우리는 당연히 없다고 말한다.
그 이유는, 우리가 보통 Spring Framework를 직접 의존성을 추가해서 웹개발을 진행하지 않고 Spring Boot를 이용해서 개발을 진행하며 `spring-boot-starter-web` 같은 스타터를 의존성에 추가한다.
해당 의존성이 바로 로깅과 관련된 의존성을 추가해 줘서 우리는 따로 로깅 구현체를 공부하지 않아도 애플리케이션 코드에서 로그를 쉽게 찍어볼 수 있었던 것이다. 위 스타터를 통해 추가되는 의존성은 다음과 같다.
spring-boot-starter-logging (자동 포함)
- Logback: logback-classic, logback-core
- SLF4J API: slf4j-api
- 브리지(모두 SLF4J로 모음)
- jul-to-slf4j : java.util.logging → SLF4J
- log4j-to-slf4j : Log4j 2 API → SLF4J
이렇게 추가하고 실제 의존성을 보면 의문점이 하나 생긴다.
"SLF4J-api 뿐만 아니라 Log4j-api도 있으면 공식문서 설명에 따라 Log4j-api가 결정되어야 하는 거 아닌가요?"
이런 의문을 해소하기 위해 직접 Spring-jcl의 자동 감지 코드 부분을 살펴보았는데 아래와 같았다.
spring-framework/spring-jcl/src/main/java/org/apache/commons/logging/LogAdapter.java at 6.2.x · spring-projects/spring-framewor
Spring Framework. Contribute to spring-projects/spring-framework development by creating an account on GitHub.
github.com

가장 먼저 Log4j가 존재하는지를 보는 것은 맞지만, 그 내부에 log4j-to-slf4j 브리지가 있으면 slf4j-api를 선택한다는 설명을 확인해 볼 수 있다.
이를 통해, 우리는 Spring-jcl과 Spring Boot Strater logger에 들어있는 의존성들이 왜 들어있는지에 대해 이해할 수 있다.
5. Logback 구현체를 선택하는 이유
그럼 이제 로깅에 대한 기본 뼈대는 이해했고 왜 Logback을 사용해야 할까?에 대해서 찾아보았다. 지금까지 살펴본 내용으로는 Logback 외에도 Log4j2와 JUL(Java Logger)가 있는 것 같은데 각각을 비교해 보고 왜 Logback을 다들 사용하는지 생각해 보았다.
먼저 Logback이다.
기본적으로 Spring Boot 기본 로그 구현체라는 점에서 나오는 방대한 커뮤니티 자료가 Logback을 선택하는 가장 큰 이유이지 않을까 생각한다. 또한 logback-spring.xml 설정파일에 대한 정보도 커스텀하기 쉽고 Profile 별 로그 appender 관리도 편리하다.
다음은 Log4j2다.
가장 큰 장점은 Async Logger를 기반으로 고부하/고빈도 대규모 서비스에서 이점을 갖는다고 하는데, 한 번도 이런 상황을 경험해보지 못해서 체감은 안된다. 단점을 찾아보자면 Spring Boot에서 사용하려면 Logback보다 시간을 더 써야 하는 것? 그리고 대부분의 자료가 Logback에 치중되어 있기에 사용하다 나오는 이슈를 직접 코드를 분석해 해결해야 할 수 있다는 부분이 첫 로그 도입에 망설이게 만드는 것 같다.
마지막은 JUL이다.
이 구현체는 3rd-party 의존성을 최소화해야 하는 환경에서만 사용할 것 같고, 기본적으로 Logback과 Log4j2가 지원하는 기능들도 지원하지 않아 직접 구현해야 하는 부분들이 많다. 그리고 위에서 사용해 보았을 때, 모든 로그가 빨간색으로 표시되는 걸 보았는데 너무 별로였다.(이 부분은 찾아보면 바꿀 수 있을지도..?)
이 세 가지만 놓고 보면 간단한 프로젝트에서는 결국 Logback을 선택할 것 같다.
지금 진행하고 있는 프로젝트에서 트레이딩 같은 초지연환경이 아니기도 하고 굳이 운영이 단순하고 자료가 풍부한 Logback을 벗어나 Log4j2를 도입하는 것은 오히려 학습 곡선을 높이는 방향이라는 생각이 들었다.
6. 다음으로
이번 글에서는 로깅에 대한 아주 기본적인 부분들을 학습해 보았다. 그냥 기본적으로 설정되는 Logback을 쓰고 바로 외부 모니터링 환경과 연동시켜 볼 수도 있었지만, 왜 Logback을 쓰는지가 궁금해 이렇게 학습했다.(결국 Logback에 대해서 자세하게 공부한 건 아니지만...)
그래도 Commons Logging에 대한 변경 이력과 Spring Framework 7부터는 로그 구현체를 선택하는 방법이 변경될 수 있다는 사실을 알 수 있었고, 애플리케이션에서 어떤 방법으로 우리가 편하게 로그를 출력하는지 자세하게 내부를 학습해 볼 수 있는 시간이었다.
다음으로, Logback-config.xml 파일을 구성하는 방법을 공부하고 이를 이용해 Profile 별 로그 출력을 설정한 후, 최종적으로 외부 모니터링 시스템에 연동하는 것을 진행해보려고 합니다. 기존에 메트릭을 수집해 모니터링하는 시스템을 Prometheus + Grafana를 이용하고 있어 Grafana를 이용하는 방식을 진행할 것 같은데 ELK와 Grafana Loki에 대해 좀 더 찾아보고 최종적으로 결정해서 도입해보려고 합니다.
'Spring Boot' 카테고리의 다른 글
| Spring Boot 로깅 (2) - logback-spring.xml 설정 및 Grafana Loki 연동 (0) | 2025.09.21 |
|---|---|
| Spring Rest Docs (3) - Validation 규칙을 API 문서에 추가하기 (restdocs-api-spec) (1) | 2025.07.08 |
| Spring Rest Docs (2) - API 문서에 디자인 입히기 (swagger, redoc) (0) | 2025.07.08 |
| Spring Rest Docs 사용하기 (1) - 기본 설정과 API 문서 생성 (0) | 2025.07.04 |
| Spring Boot에서 발생한 TimeZone 문제 (0) | 2025.05.04 |