🪛 한계점
스프링 어플리케이션이 띄우는 서버 정보에 대해 가독성이 부족하며, 추적 가능한 로그 기능이 필요하다.
📂 목차
📚 본문
Log4j2
우선 Log4j2 는 Asynchronous Logger + LMAX Disruptor 기반으로 1800만 건/s 로깅이 가능하며,
- XML/JSON/YAML 설정 등을 지원
- Lambda 기반 지연 로깅
- MDC, Marker, 사용자 정의 Message API
등의 고급 기능들을 지원한다. 사용하려면 다음 의존성을 추가해준다.
dependencies {
...
// log4j2
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
...
}
기존에 있는 starter 팩에 Logback과의 충돌을 막기 위해 다음을 추가한다.
configurations.all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
log4j2.properties
log4j2 는 기본적으로 resources에 log4j2.properties
를 참조하여 로깅 구성 설정을 할 수 있다. 기본적으로 Logging을 할 때의 기능들을 수행하는 컴포넌트 Appender, Logger 가 있다.
아래의 용어들을 보고 오길 바란다.
로깅 레벨 계층 와 이름 설정
로그를 띄울 때도 특정 정보들만 띄울 수 있도록 할 수 있다.
로그 레벨 계층은 다음과 같다
ALL → TRACE → DEBUG → INFO → WARN → ERROR → FATAL → OFF
- ALL: 모든 로그 레벨 허용
- TRACE: 가장 세밀한 단계(디테일 메서드 호출, 루프 등)
- DEBUG: 개발 및 디버깅 시 유용한 정보-변수 상태, 흐름 등
- INFO: 운영 중 기본적으로 남기는 일반 정보-시작/종료, 상태 전환 등
- WARN: 문제는 아니지만 주의가 필요한 상황-성능 저하, 비추천 API 사용 등
- ERROR: 기능 일부 실패 등 처리 중에 장애 발생
- FATAL: 치명적인 오류로 어플리케이션 종료 수준
- OFF: 모든 로그를 비활성화
status 의 프로퍼티를 주면 해당 status 보다 하위의 로그 메시지들을 출력하게 된다.
## log4j2.properties
# ▼ 내부 로깅 레벨
status = warn
name = PropertiesConfig
name으로는 로깅 설정 전체의 이름을 지정하는 식별자이다.
Filter 기능을 사용한 로그 이벤트 상세 제어
Log4j2는 필터를 사용하여 어떻게 처리해야 할지 제어할 수 있다.
우선 이벤트를 평가하는 것부터 보자면 기본적인 필터 로직은 로그 이벤트를 평가하고, ACCEPT, DENY, NEUTRAL 중 하나의 결과를 반환하게 된다.
- ACCEPT: 해당 로그를 즉시 수용하고 다음 필터는 검사하지 않음(바로 출력)
- DENY: 해당 로그를 즉시 버리고 이후 단계로 전달하지 않음(버림)
- NEUTRAL: 판단을 보류하고 다음 필터로 넘김(다음 필터한테 인계)
위 3가지를 결정내리는 단계는 크게 4단계로 나뉘고, 각 필터 단계에서 위의 결과 중 하나를 가지게 된다.
- Context-wide: 전체 설정에 대한 초기 필터
- Logger-level: 특정 로거에 설정된 필터
- AppenderReference: 어떤 Appender에 보낼지 결정
- Appender-level: 실제 Appender 내부 필터
아래 코드는 threshold 라는 필터를 통해 전역 필터를 설정하고 있다.
# ▼ 전역 필터 설정: 디버그 이상 로그만 출력
filters = threshold # 전역필터 이름은 threshold
# filters 뒤에 나열된 필터 이름은 이후 설정에서
# filter.<name>.type, filter.<name>.level 같이 참조 가능
# 전역 필터 threshold 의 타입으로 ThresholdFilter 라는 레벨 기반 필터를 사용한다.
filter.threshold.type = ThresholdFilter
# 메시지의 레벨이 설정한 기준 이상인지 비교하여 처리한다.
# 해당 ThresholdFilter 가 적용할 기준 레벨을 정의한다.
filter.threshold.level = debug
# 기본적으로 onMatch=NEUTRAL, onMismatch=DENY 이므로
# DEBUG 이상이면 NEUTRAL로 넘어가 다음 레벨 검사 후 출력 가능
# TRACE 이하는 DENY로 바로 차단됨
대표적인 필터 종류로는
- ThresholdFilter: 특정 기준 이상인지 아닌지 필터링
- BurstFilter
- CompositeFilter
- DynamicThresholdFilter
- RegexFilter: 정규표현식을 사용한 필터링
- MapFilter, MarkerFilter, TimeFilter 등등 등이 있다.
Appenders 구성하기
# ▼ 콘솔 Appender 구성
# 사용할 Appender 나열
appenders = console, rolling
# Appender 의 타입 지정, 여기서는 Console로, System.out, System.err 같은 출력 형식으로 내보냄
appender.console.type = Console
# Appender 에 식별자를 붙임
appender.console.name = STDOUT
# Appender 에 로그 메시지를 어떤 형식으로 변환할지 정함
appender.console.layout.type = PatternLayout
# Appender 의 로그 출력 포맷을 지정함
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5p %c{1}:%L - %m%n
로그 출력 포맷
- %d{…}: 날짜/시간의 포맷 지정
- %t: 스레드 이름(main 등)
- %-5p: 고정 폭 5자리의 로그 레벨(INFO, DEBUG 등)
- %c{1}: 로거 이름의 마지막 컴포넌트, 일반적으로 클래스 명
- %L: 로그 발생 코드 라인 번호
- %m: 실제 메시지
- %n: 줄바꿈
# ▼ 롤링 파일 Appender 구성
# 로그 파일 내용을 어디 저장할지 지정
appender.rolling.fileName = logs/app.log
# 파일명을 어떻게 쓸건지 설정
appender.rolling.filePattern = logs/app-%d{yyyy-MM-dd}-%i.log.gz
# Policies 라는 롤오버 조건 그룹(wrapper)를 지정
appender.rolling.policies.type = Policies
# 시간 기반 트리거 정책 사용, 특정 주기마다 로그 파일이 자동으로 Rollover
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
# Rollover 간격을 1단위로 지정
appender.rolling.policies.time.interval = 1
# 파일 크기 기반 트리거 정책 지정
appender.rolling.policies.size.type = SizeBasedTriggeringPolicy
# 파일이 10MB를 초과 시 Rollover 실행
appender.rolling.policies.size.size = 10 MB
# Rollover 전 후의 처리 전략을 지정
appender.rolling.strategy.type = DefaultRolloverStrategy
# Rollover 파일의 최대 개수를 7개로 설정
appender.rolling.strategy.max = 7
루트 로거 설정 (기본 레벨 및 Appender 연결)
# ▼ 루트 로그 레벨 및 Appender 참조
# 로그 레벨 설정
rootLogger.level = info
# 기본 로그 레벨과 appender 매핑 설정
rootLogger.appenderRefs = stdout, rolling
rootLogger.appenderRef.stdout.ref = STDOUT
rootLogger.appenderRef.rolling.ref = ROLLING
Logger 객체를 가져와 출력해보기
Logging 메시지를 출력할 클래스 컨텍스트에 다음을 입력한다:
import org.slf4j.*;
...
private static final Logger logger =
LoggerFactory.getLogger(StudyApplication.class);
출력할 메시지가 살아있는 function 의 context 에서 다음을 입력한다.
ConfigurableApplicationContext applicationContext =
springApplication.run(args);
logger.info("The application is completely running");
다음이 출력됨을 볼 수 있다.
[restartedMain] INFO StudyApplication:73 - The application is completely running
✒️ 용어
Logger
한 개 이상의 Appender 를 사용하여 로그 메시지 표시를 담당하는 로깅 프레임워크의 컴포넌트
Appender
어펜더를 사용하여 로그가 출력되는 대상과 로깅 포맷을 지정할 수 있다. 로그 메시지가 출력되는 매체에 따라 다양한 어펜더가 있고, 콘솔 어펜더는 어플리케이션의 콘솔에 로그를 출력하고, 파일 어펜더는 로그 메시지를 파일에 출력한다.
- RollingFileAppender: 시간과 날짜 기반으로 별도의 파일에 로그를 출력하게 된다.
- SMTP Appender: 정해진 이메일 주소로 로그를 출력한다.