// PrintStream의 실제 구현
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
System.out.println()은 내부적으로 동기화되어 있다. 멀티스레드 환경에서 여러 스레드가 동시에 출력을 시도하면
등의 문제가 있다.
// 콘솔 출력은 블로킹 I/O
for (int i = 0; i < 10000; i++) {
System.out.println("Processing: " + i); // 매번 I/O 대기
}
콘솔 출력은 블로킹 I/O이다.
등의 문제가 있다.
// 문제가 있는 코드
public void processOrder(Order order) {
System.out.println("주문 처리 시작: " + order.getId());
// ... 비즈니스 로직
System.out.println("주문 처리 완료");
}
개발 환경에서는 디버깅에 유용하지만 운영 환경에서는 모든 로그가 출력된다.
로그를 끄거나 레벨을 조정할 방법이 없고, 코드 수정 없이는 제어가 불가능하다.
// 올바른 접근
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public void processOrder(Order order) {
log.debug("주문 처리 시작: {}", order.getId()); // 개발 환경에서만 출력
// ... 비즈니스 로직
log.info("주문 처리 완료: {}", order.getId()); // 운영 환경에서도 출력
}
// System.out.println 사용
System.out.println("사용자 로그인: " + userId);
// 출력: 사용자 로그인: user123
System.out.println을 사용하면
타임스탬프 없음
로그 레벨 정보 없음
스레드 정보 없음
클래스/메서드 위치 정보 없음
JSON 파싱 불가능
등의 문제점이 있다.
// 로깅 프레임워크 사용
log.info("사용자 로그인: userId={}", userId);
// 출력:
// 2026-02-05 10:23:45.123 [http-nio-8080-exec-1] INFO c.e.service.AuthService - 사용자 로그인: userId=user123
이렇게 로깅 프레임워크를 사용하면
타임스탬프로 시간 추적 가능
스레드 이름으로 동시성 이슈 디버깅 가능
클래스명으로 로그 출처 파악
ELK 스택 등으로 로그 수집 및 분석 가능
등이 가능해진다.
// TRACE: 매우 상세한 정보 (거의 사용 안 함)
log.trace("메서드 진입: parameter={}", param);
// DEBUG: 개발 중 디버깅 정보
log.debug("캐시 조회: key={}, hit={}", key, cacheHit);
// INFO: 중요한 비즈니스 이벤트
log.info("사용자 가입: userId={}, email={}", userId, email);
// WARN: 잠재적 문제 상황
log.warn("API 응답 지연: responseTime={}ms", responseTime);
// ERROR: 에러 상황 (반드시 예외 객체 포함)
log.error("결제 처리 실패: orderId={}", orderId, exception);