자바 기본 로깅 시스템 이해하기
자바는 1.4.2 버전부터 java.util.logging 패키지를 통해 내장형 로깅 기능을 제공합니다. 이 API의 핵심은 Logger 클래스이며, 애플리케이션의 문제 진단 및 실행 흐름 추적에 매우 유용합니다. 서버 장애나 예외 발생 시, 로그를 통해 원인을 정확히 파악할 수 있습니다.
로그 레벨은 총 8단계로 구성되며, 우선순위는 다음과 같습니다: SEVERE > WARNING > INFO > CONFIG > FINE > FINER > FINEST > ALL. 숫자 값으로는 더 높은 우선순위일수록 작은 값을 가지며, 현재 설정된 레벨보다 낮은 중요도의 메시지는 기록되지 않습니다. 예를 들어, 레벨이 WARNING으로 설정되면 INFO 메시지는 출력되지 않습니다.
주요 구성 요소
- Logger: 로그 기록 요청을 처리하는 주체입니다.
- Level: 로그의 중요도를 정의합니다.
- Handler: 로그 출력 방식을 결정합니다 (콘솔, 파일 등).
- Formatter: 로그 출력 형식을 지정합니다.
- Filter: 조건에 따라 특정 로그만 기록할 수 있도록 필터링합니다.
- LogRecord: 로그 항목 하나를 표현하는 데이터 객체입니다.
동작 구조 분석
로깅 시스템은 LogManager 싱글턴 인스턴스를 기반으로 초기화됩니다. 최상위 노드인 root logger가 생성되며, 나머지 Logger 객체는 필요 시 지연 생성(lazy loading)됩니다. 이는 자원 절약과 성능 향상을 위한 전략입니다.
다음 코드를 살펴보겠습니다:
public class LoggingExample {
public static void main(String[] args) {
Logger logger = Logger.getLogger("com.example.app");
logger.info("애플리케이션이 시작되었습니다.");
}
}
내부 동작 흐름
Logger.getLogger()호출 시,LogManager에서 기존 인스턴스 검색 또는 새로 생성합니다.- 해당 Logger의 레벨(
levelValue)과 요청된 메시지 레벨을 비교합니다. 통과하면 다음 단계로 진행됩니다. LogRecord객체를 생성하여 메시지, 타임스탬프, 클래스/메서드 정보 등을 담습니다.- 등록된
Filter가 있다면isLoggable()메서드로 추가 검증을 수행합니다. - 현재 Logger와 부모 계층 구조 상 모든 Handler에게 publish 요청을 전달합니다.
Filter를 통한 고급 제어
기본 레벨 외에도 사용자 정의 필터를 적용하여 보다 세밀한 제어가 가능합니다. 아래는 특정 패키지의 로그만 허용하는 필터 예제입니다:
public class PackageFilter implements Filter {
private final String allowedPackage;
public PackageFilter(String packageName) {
this.allowedPackage = packageName;
}
@Override
public boolean isLoggable(LogRecord record) {
String loggerName = record.getLoggerName();
return loggerName != null && loggerName.startsWith(allowedPackage);
}
}
// 사용 예시
Logger appLogger = Logger.getLogger("com.example.service");
appLogger.setFilter(new PackageFilter("com.example"));
Handler 종류와 활용
다양한 출력 대상에 맞는 Handler를 제공하며, 하나의 Logger에 여러 Handler를 연결할 수 있습니다.
대표적인 Handler 설명
- ConsoleHandler:
System.err로 출력. 개발 중 디버깅에 적합. - FileHandler: 로컬 파일에 저장. 용량 제한 및 회전(rotaion) 기능 지원.
- SocketHandler: 원격 서버로 로그 스트리밍 전송.
- MemoryHandler: 메모리 버퍼에 임시 저장 후 조건 충족 시 다른 Handler로 전달.
MemoryHandler 예제
중요 오류(SEVERE) 발생 시에만 로그를 파일로 저장하고 싶은 경우:
FileHandler fileHandler = new FileHandler("errors.log");
MemoryHandler memoryHandler = new MemoryHandler(
fileHandler, // 대상 핸들러
100, // 버퍼 크기
Level.SEVERE, // flush 트리거 레벨
true // push 액션 후 flush 여부
);
Logger logger = Logger.getLogger("critical.module");
logger.addHandler(memoryHandler);
설정 파일: logging.properties
JVM 전체 또는 애플리케이션별로 logging.properties 파일로 기본 설정을 관리할 수 있습니다. 위치는 일반적으로 $JAVA_HOME/jre/lib/logging.properties이며, 커스텀 파일은 시스템 속성으로 지정 가능합니다:
java -Djava.util.logging.config.file=mylogging.properties MyApp
예제 설정 내용:
handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
.level=INFO
# 콘솔 출력 설정
java.util.logging.ConsoleHandler.level=WARNING
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
# 파일 출력 설정
java.util.logging.FileHandler.pattern=app.%u.%g.log
java.util.logging.FileHandler.limit=10485760
java.util.logging.FileHandler.count=5
java.util.logging.FileHandler.formatter=java.util.logging.XMLFormatter
# 특정 로거 레벨 조정
com.example.service.level=FINE
이 설정은 전역 레벨을 INFO로 유지하면서, 콘솔에는 WARNING 이상만 출력하고, 파일에는 XML 형식으로 상세 기록을 남기는 복합 전략을 구현합니다.