Java/일반
Java Logging
창욱씨
2020. 4. 3. 14:30
반응형
1. Java Logging 개요
Java Logging은 애플리케이션의 실행 이벤트, 오류, 디버깅 정보를 기록하는 메커니즘입니다.
- ✅ 문제 진단 : 운영 중 발생한 오류 추적
- ✅ 모니터링 : 시스템 상태 실시간 파악
- ✅ 감사(Audit) : 보안 이벤트 및 사용자 행동 기록
2. 주요 로깅 프레임워크
| 프레임워크 | 역할 | 특징 |
|---|---|---|
| java.util.logging (JUL) | 구현체 | JDK 기본 제공, 별도 의존성 없음 |
| Log4j 2 | 구현체 | 고성능, 비동기 로깅 지원 |
| Logback | 구현체 | SLF4J 네이티브 구현체, Spring Boot 기본 |
| SLF4J | 추상화(Facade) | 인터페이스만 제공, 직접 출력 불가 |
3. 로그 레벨 상세 설명
로그 레벨은 메시지의 중요도를 나타내며, 설정된 레벨 이상의 로그만 출력됩니다.
예) 레벨을
INFO로 설정 →INFO,WARN,ERROR,FATAL만 출력 (TRACE/DEBUG 무시)
🔹 레벨 계층 구조 (낮음 → 높음)
TRACE < DEBUG < INFO < WARN < ERROR < FATAL
(상세) (치명)
🔹 각 레벨 상세
| 레벨 | 용도 | 사용 예시 | 권장 환경 |
|---|---|---|---|
| TRACE | 가장 상세한 흐름 추적 | 메서드 진입/종료, 루프 내부 변수값 | 개발, 특정 이슈 디버깅 |
| DEBUG | 개발 중 디버깅 정보 | SQL 쿼리, 파라미터 값, 분기 조건 | 개발/스테이징 |
| INFO | 정상적인 주요 이벤트 | 앱 시작, 사용자 로그인, 주문 완료 | 운영 (기본값) |
| WARN | 잠재적 문제 경고 | Deprecated API, 재시도 발생, 임계치 근접 | 운영 |
| ERROR | 오류 발생 (앱은 계속 실행) | 외부 API 실패, DB 저장 실패 | 운영 (모니터링 필수) |
| FATAL | 치명적 오류 (앱 중단 수준) | 커넥션 풀 고갈, 메모리 부족 | 운영 (즉시 알림) |
⚠️ 주의:
FATAL은 Log4j/Log4j2 전용입니다.
SLF4J와 Logback에는 FATAL이 없으며,ERROR로 통합되어 있습니다.
🔹 레벨별 코드 예시
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PaymentService {
private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);
public void pay(String userId, int amount) {
logger.trace("pay() 메서드 진입 - userId={}, amount={}", userId, amount);
logger.debug("결제 요청 데이터 검증 중: userId={}", userId);
if (amount > 1_000_000) {
logger.warn("고액 결제 감지: userId={}, amount={}", userId, amount);
}
try {
// 결제 처리 로직
logger.info("결제 성공: userId={}, amount={}", userId, amount);
} catch (PaymentException e) {
logger.error("결제 실패: userId={}", userId, e);
} catch (OutOfMemoryError e) {
// Log4j2에서는 logger.fatal(...) 사용 가능
logger.error("[FATAL] 시스템 자원 고갈, 즉시 조치 필요", e);
}
}
}
🔹 레벨 선택 가이드라인
🟢 INFO를 써야 할 때
- 사용자가 사후에 봤을 때 의미 있는 비즈니스 이벤트
- 예: "사용자 가입 완료", "배치 작업 시작/종료"
🟡 WARN을 써야 할 때
- 당장 문제는 없지만 방치하면 ERROR로 발전할 수 있는 상황
- 예: "캐시 미스율 80% 초과", "재시도 3회 중 1회 실패"
🔴 ERROR를 써야 할 때
- 비즈니니스 로직이 정상 완료되지 못한 경우
- 반드시 예외 객체를 함께 전달:
logger.error("메시지", exception) - 운영 환경에서는 알림(Slack, PagerDuty 등) 과 연동 권장
🔵 DEBUG/TRACE 사용 시 주의
- 운영에서 활성화하면 성능 저하 + 디스크 폭증 발생
- 반드시 Parameterized 형태(
{})로 작성 - 운영에서 임시로 켤 때는 특정 패키지만 한정해서 적용
<!-- 운영 중 특정 패키지만 DEBUG로 임시 변경 -->
<configuration>
<logger name="com.example.payment" level="DEBUG"/>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
🔹 환경별 권장 레벨 설정
| 환경 | 권장 레벨 | 이유 |
|---|---|---|
| 로컬 개발 | DEBUG |
모든 동작 확인 |
| 개발 서버(DEV) | DEBUG |
통합 디버깅 |
| 스테이징(STG) | INFO |
운영과 동일 조건 검증 |
| 운영(PROD) | INFO 또는 WARN |
성능·디스크 보호 |
4. 핵심 구성 요소
| 구성 요소 | 설명 |
|---|---|
| Logger | 로그 생성 주체 (보통 클래스 단위) |
| Appender | 출력 대상 (Console, File, DB, Kafka 등) |
| Layout / Encoder | 출력 형식 정의 (시간, 스레드, 메시지 등) |
| Filter | 조건부 필터링 |
| MDC | 요청 추적 컨텍스트 (예: requestId) |
5. SLF4J가 다른 프레임워크와 다른 점
💡 SLF4J는 "구현체"가 아니라 "인터페이스(Facade)"입니다.
🔹 구조
[애플리케이션]
↓ SLF4J API 호출
[ SLF4J ] ← 추상화 계층 (Facade)
↓ 바인딩
[Logback / Log4j2 / JUL] ← 실제 구현체
🔹 비유로 이해하기
- SLF4J = JDBC API
- Logback / Log4j2 = MySQL / Oracle 드라이버
🔹 SLF4J의 장점
- 구현체 교체 자유 — 의존성만 바꾸면 코드 수정 불필요
- Parameterized Logging —
logger.info("user: {}", name)형태로 성능·안전성 확보 - 라이브러리 호환성 — Spring, Hibernate, Kafka 등 표준 채택
- 레거시 브리지 지원 —
log4j-over-slf4j,jcl-over-slf4j등
🔹 의존성 예시 (Maven)
<!-- SLF4J API -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
<!-- 구현체: Logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>반응형