Logging
🔍 Java Logging System 완전 정복: JUL, SLF4J, Logback, Log4j까지
📌 들어가며
소프트웨어에서 로깅(logging)은 필수입니다. 디버깅, 모니터링, 감사, 보안, 성능 분석 등 모든 영역에서 로그는 시스템의 눈과 귀 역할을 합니다.
Java에는 기본 로깅 API인 java.util.logging
부터, 다양한 서드파티 라이브러리인 Log4j, Logback, SLF4J까지 수많은 로깅 프레임워크가 존재합니다. 이 글에서는 다음과 같은 주제로 Java 로깅 시스템을 깊이 있게 다뤄보겠습니다:
🗂️ 목차
- 자바 로깅 시스템의 개요
java.util.logging
(JUL)- SLF4J (Simple Logging Facade for Java)
- Log4j & Log4j2
- Logback
- 로깅 프레임워크 비교
- 로깅 구성 방법 및 Best Practice
- 실전 예제: Spring Boot에서 로깅 설정
- 로깅과 성능, 보안, 운영에서의 팁
1️⃣ 자바 로깅 시스템의 개요
🔸 로깅이란?
- 정의: 애플리케이션의 실행 중 발생하는 이벤트(정보, 경고, 에러 등)를 기록하는 행위
- 목적: 디버깅, 감사, 모니터링, 성능 분석 등
- 로깅 수준(Level):
TRACE
→DEBUG
→INFO
→WARN
→ERROR
→FATAL
- Level이 높을수록 더 중요한 이벤트(FATAL이 가장 높은 레벨)
2️⃣ java.util.logging
(JUL)
🔹 소개
JDK 1.4부터 포함된 Java 기본 로깅 프레임워크입니다.
import java.util.logging.*;
public class JulLoggerExample {
private static final Logger logger = Logger.getLogger(JulLoggerExample.class.getName());
public static void main(String[] args) {
logger.info("정보 로그입니다.");
logger.warning("경고 로그입니다.");
logger.severe("심각한 에러 로그입니다.");
}
}
🔹 구성 요소
구성 요소 | 설명 |
---|---|
Logger |
로깅의 핵심 클래스 |
Handler |
로그를 어디로 출력할지 지정 (콘솔, 파일 등) |
Formatter |
로그 출력 형식 지정 (SimpleFormatter , XMLFormatter ) |
Level |
로그 수준 지정 (INFO , WARNING , ...) |
🔹 설정 파일 (logging.properties)
handlers= java.util.logging.ConsoleHandler
.level= INFO
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
JVM 실행 시 -Djava.util.logging.config.file=logging.properties
로 설정 가능
3️⃣ SLF4J: Logging Facade
🔹 SLF4J란?
Simple Logging Facade for Java
- 직접 로깅을 하지 않고, 다른 로깅 프레임워크를 추상화한 라이브러리
- Logback, Log4j, java.util.logging 등 어떤 구현체든 붙일 수 있음
🔹 장점
- 로깅 API를 표준화해 다양한 환경에서 유연하게 적용 가능
Logger
API는 매우 단순하고 직관적
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jExample {
private static final Logger log = LoggerFactory.getLogger(Slf4jExample.class);
public static void main(String[] args) {
log.info("SLF4J Info");
log.debug("SLF4J Debug");
log.error("SLF4J Error");
}
}
🔹 구현체 선택 예시
구현체 | 의존성 라이브러리 |
---|---|
Logback | logback-classic |
Log4j | log4j-slf4j-impl |
JUL | jul-to-slf4j |
4️⃣ Log4j & Log4j2
🔹 Log4j
- Apache에서 만든 가장 널리 알려진 로깅 라이브러리
log4j.properties
나log4j.xml
로 설정
log4j.rootLogger=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
🔹 Log4j2의 개선점
- 비동기 로깅 지원으로 성능 향상
- 플러그인 기반 설정
- Property Substitution, JSON/YAML 설정 지원
<!-- log4j2.xml 예시 -->
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
5️⃣ Logback
🔹 개요
- SLF4J의 공식 구현체
- 성능 좋고, Spring Boot 기본 로깅 구현체
- 설정 파일:
logback.xml
또는logback-spring.xml
🔹 Logback 구성요소
구성 요소 | 설명 |
---|---|
<appender> |
출력 대상 (Console, File, RollingFile 등) |
<encoder> |
로그 포맷 |
<logger> |
개별 클래스 또는 패키지의 로깅 설정 |
<root> |
전체 로거에 대한 설정 |
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>
6️⃣ 로깅 프레임워크 비교
항목 | JUL | Log4j | SLF4J | Logback | Log4j2 |
---|---|---|---|---|---|
JDK 포함 여부 | ✅ 기본 포함 | ❌ 외부 추가 필요 | ❌ (Facade) | ❌ | ❌ |
구성 방식 | properties | properties/xml | facade only | xml/groovy | xml/yaml/json |
성능 | 낮음 | 중간 | - | 높음 | 매우 높음 |
비동기 지원 | ❌ | ❌ | ❌ | ✅ | ✅ (Async Appender) |
Spring Boot 기본 | ❌ | ❌ | ✅ | ✅ | ❌ (옵션으로 교체 가능) |
7️⃣ 로깅 구성 방법 및 Best Practices
✅ SLF4J + Logback 조합 추천 (Spring Boot에서 디폴트)
✨ Best Practices
- Logger는 클래스당 하나
private static final Logger log = LoggerFactory.getLogger(YourClass.class);
- 문자열 결합은 지양
log.debug("Result: {}", result); // O log.debug("Result: " + result); // X (성능 낭비)
- 적절한 로그 레벨 설정
- 개발환경에서는
DEBUG
, 운영환경에서는INFO
이상
- 개발환경에서는
- 민감한 정보는 절대 기록하지 말 것
- 주민번호, 비밀번호, 토큰 등
8️⃣ 실전 예제: Spring Boot에서 로그 설정
기본 설정 (application.properties
)
logging.level.root=INFO
logging.level.com.example=DEBUG
logging.file.name=app.log
고급 설정 (logback-spring.xml
)
<configuration scan="true">
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/myapp.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
9️⃣ 로깅 관련 실전 팁 (운영 중심)
📌 성능
DEBUG
레벨은 운영환경에서 비활성화- 비동기 로깅 적용 시 대량 로그 처리 가능 (Logback AsyncAppender, Log4j2 AsyncLogger)
📌 보안
- 로그에 민감한 정보 마스킹 (
password=****
) - IP, 사용자 정보 등의 감사 추적용 로그는 별도 파일로 분리
📌 운영
- 로그 롤링(Rolling) 필수
- ELK(Stack), Prometheus + Grafana 연동 시 JSON 포맷 추천
📌 그런데 log4j-to-slf4j 란?
당신의 프로젝트는 SLF4J + Logback을 기반으로 로그를 통합하고 있는데,
서드파티 라이브러리가 위처럼 Log4j 2 API를 직접 사용하고 있다면?
👉 그 로그는 SLF4J나 Logback 설정을 따르지 않고, Log4j 자체 설정이나 기본 설정으로 출력됩니다.
이러면 로그가 혼란스럽고, 출력 형식이나 레벨 제어도 통일되지 않게 됩니다.
🎯 해결책: log4j-to-slf4j
이럴 때 log4j-to-slf4j 브릿지를 사용하면:
Log4j 2 API를 사용해 작성된 코드에서 출력한 로그가 SLF4J로 전달되고,
SLF4J의 구현체(예: Logback)가 최종적으로 로그를 출력합니다.
🔧 브릿지 예시 흐름
Log4j API (Logger, LogManager)
↓
log4j-to-slf4j
↓
SLF4J API
↓
Logback (최종 출력)
🧾 마무리
Java의 로깅 시스템은 단순히 콘솔에 메시지를 출력하는 도구가 아닙니다. 운영 환경에서의 생존과 직결되는 필수 요소입니다. 로깅 프레임워크의 선택과 구성은 애플리케이션의 디버깅, 보안, 성능 분석까지도 좌우할 수 있으므로, 이번 포스팅을 통해 로깅 시스템에 대한 탄탄한 기초와 실전 지식을 쌓아가시길 바랍니다.