-
[My-Books] Logback 적용기 - 설정해야겠지?개발 ing 2025. 3. 16. 17:44
해당 글은 NHN Academy 프로젝트 과정 My-books 서비스를 만들면서 했던 내용이다
이 글은 아래 포스트에서 부터 이어진다
https://hyeonni.tistory.com/112
[My-Books] Logback 적용기 - Log의 중요성
해당 글은 NHN Academy 프로젝트 과정 My-books 서비스를 만들면서 했던 내용이다 지금까지 개발을 하면서 가장 많이 듣고 가장 많이 했던 말이 있다 "에러가 나거나 하면 디버깅을 하지말고 우선 로
hyeonni.tistory.com
Spring Logback 설정하기에 앞서서...
일단 Java에서 흔히 사용하는 방식은 lombok라이브러리의 SLF4J(심플 로딩 파사드 for Java) 이다.
SLF4J 이름 그대로 로깅 시스템에 대한 추상화를 제공한다
SLF4J는 추상 로깅 프레임워크이기 때문에 단독으로는 사용하지 못함.
즉, 최종 사용자가 배포시 원하는 로깅 프레임워크를 결정하고 사용해도 SLF4J가 인터페이스화 되어있기에, SLF4J를 의존하는 클라이언트 코드에서는 실제 구현을 몰라도 된다 (의존관계 역전 법칙).
그러면 구현체가 있어야한다
구현체로는 Logback, Log4j, java.util.logging 이 있다
우리는 이 세개의 구현체중에 Logback이라는 것을 사용할 것이다
Logback
- 성능 : 매우 뛰어난 성능을 가지고 있다. 특히 비동기 로깅 기능을 통하여 빠른 로깅을 지원한다.
- 구성의 유연성 : XML 기반의 구성파일을 통하여 설정을 유연하게 변경이 가능하다.
- 로깅레벨 : Logback은 다양한 로깅 레벨을 지원하여 당양한 수준의 로그를 다룰 수 있다.
- log4j와의 성능을 비교하면 logback이 월등하다는 평가가 많다고 한다.
- 빠른 implementation
- 적은 메모리 점유
- maxHistory 설정 값을 이용해 일정 기간이 지나면 로그파일 자동 삭제
등등의 다양한 이점으로 인하여 Logback으로 설정을 할 것이다.
로그 레벨은 기본적으로 [trace > debug > info > warn > error] 순이고 디폴트 설정은 info 로 되어 있다.
만약 설정을 info로 한다면 -> info의 하위 단계인 trace와 debug는 기록 되지 않는다
Log level 이란
- 로그 메시지의 중요도를 나타내는 수준을 의미
- TRACE
- 가장 상세한 로그 레벨로, 애플리케이션의 실행 흐름과 디버깅 정보를 상세히 기록한다. 주로 디버깅 시에 사용된다.
- DEBUG
- 디버깅 목적으로 사용되며, 개발 단계에서 상세한 정보를 기록한다.
- 애플리케이션의 내부 동작을 이해하고 문제를 분석하는 데 도움을 준다.
- INFO
- 애플리케이션의 주요 이벤트나 실행 상태에 대한 정보를 전달한다.
- WARN
- 경고성 메시지를 기록한다.
- 예상치 못한 문제나 잠재적인 오류 상황을 알리는 메시지이다.
- ERROR
- 오류 메시지를 기록한다.
이 글에서 가장 중요한건 개발환경 마다 Log level을 다르게 설정해줘야 한다.
개발 환경별 Log Level 설정의 중요성
흔히 환경은 개발 환경, 테스트 환경, 운영 환경으로 나뉘어 진다.
각 환경마다 로그레벨을 다르게 설정하는 것이 중요하다.
1. 개발 환경 (Development)
- DEBUG 또는 TRACE 레벨 사용
- 개발 단계에서는 가능한 한 많은 정보를 기록하는 것이 중요하다.
- DEBUG나 TRACE 레벨을 사용하여 프로그램의 흐름을 추적하고 예상치 못한 동작을 분석할 수 있다.
- 이를 통해 개발자가 디버깅할 때 최대한의 정보를 얻을 수 있다.
2. 테스트 환경 (Test)
- INFO 또는 WARN 레벨 사용
- 테스트 환경에서는 중요한 시스템 이벤트를 기록하는 것이 목적이다.
- INFO 레벨을 사용하여 테스트 실행 과정에서의 주요 이벤트를 기록하고, 예상하지 못한 문제가 발생할 경우 WARN 레벨의 메시지를 남겨야 한다.
- 너무 자세한 로그는 테스트 성능을 저하시킬 수 있으므로 디버깅에 불필요한 DEBUG나 TRACE 로그는 배제하는 것이 좋다.
3. 운영 환경 (Production)
- WARN 또는 ERROR 레벨 사용
- 운영 환경에서는 로그가 너무 많으면 성능에 영향을 줄 수 있다.
- WARN 이상으로 설정하면 주요 경고 및 오류 로그만 남아, 시스템 안정성을 유지하면서 필요한 문제를 추적할 수 있다.
- ERROR 레벨을 사용하면 치명적인 오류가 발생했을 때만 기록하여 장애 분석에 집중할 수 있다.
그럼 왜 운영 환경에서 로그가 너무 많으면 성능에 영향을 줄 수 있을까?
1. 디스크 I/O 증가
로그 파일을 저장하기 위해서는 디스크에 계속 데이터를 기록해야 한다. 로그가 많아지면 디스크 I/O 작업이 증가하여 애플리케이션의 응답속도가 느려질 수 있다.
2. CPU 사용량 증가
로그를 생성한느 과정에서 문자열 변환, 파일 입출력 등의 연산이 필요하다. DEBUG, TRACE 레벨의 상세한 로그가 많아지면 CPU 리소스를 더 많이 사용하게 된다.
등등의 이유로 운영 환경에서는 서비스 운영에 꼭 필요한 정보만 남기도록 한다.
Java Backend 에서 로깅 설정하는 방법
두가지 방법이 있다. xml, properties 설정
그럼 어떤 설정방법을 사용할까?
XML vs properties 설정 선택 기준
- XML 설정: 복잡한 로깅 설정이 필요한 경우 (여러 Appender 설정, 다양한 필터 적용)
- properties 설정: 간단한 로깅 설정이 필요한 경우 (기본적인 로그 레벨 및 출력 설정)
그래서 여기 글에서는 xml 설정 방법을 좀 더 깊이 있게 알려줄 것이다.
properties 설정하는거는 글 가장 하단에 둘것이다.
xml 파일로 기본적인 로깅 하는 방법
<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%msg%n</pattern> </encoder> </appender> <logger name="com.example.package" level="debug"> <appender-ref ref="CONSOLE"/> </logger> <root level="info"> <appender-ref ref="CONSOLE"/> </root> </configuration>
- 우선 xml 설정에서 주요 컴포넌트로는 appender와 logger 두가지로 나뉘어 진다.
- appender : logger 출력 위치를 설정한다. console, file, DB 등 지정할 수 있다.
- logger : 어떤 패키지 또는 클래스에서 어떤 레벨의 로그를 출력할지를 설정한다.
- root 는 자바에서 if문의 else와 같은 역할을 한다고 생각하면 된다.
Log pattern
- 로그 메시지를 어떤 형식으로 출력할지 지정하는 문자열이다.
종류
- [%d{yyyy-MM-dd HH:mm:ss.SSS}] : 로그 이벤트가 발생한 날짜 및 시간
- [%thread] : 현재 스레드의 이름
- [%X{traceId}] : MDC (Mapped Diagnostic Context)에서 "traceId"라는 키로 설정된 값의 출력
- [%-5level] : 로그 레벨을 나타냅니다. 여기서는 왼쪽 정렬된 5자리의 공간을 할당하고 레벨을 표시
- [%logger] : 로거의 이름
- [%m] : 실제 로그 메시지
- [%n] : 새로운 줄을 표시
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
순서대로 시간, 스레드 이름, 로그레벨, 로그의 이름, 실제 로그메시지, 새로운 줄을 표시 등을 나타낸다.
로그 파일 저장
<appender name="INFO_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>./logs/info.log</file> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <charset>utf8</charset> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread [%X{traceId}] %-5level %logger - %m%n</Pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>./was-logs/info.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>10MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>100</maxHistory> </rollingPolicy> </appender>
- RollingFileAppender 를 사용할건데 FileAppender를 상속하여 로그 파일을 rollover 한다.
- file 이라는 걸 이용하여 경로와 이름을 지정한다.
- filter 로그 이벤트를 필터링하는데 사용되며, 여기서는 [LevelFilter] 를 사용하여 INFO 레벨 이상의 로그만 해당 appender에 기록하도록 설정했다.
- encoder 인코딩을 어떻게 할지 설정하는 것이다 pattern를 사용하였다.
- .rollingPolicy 로그 파일의 롤링 정책을 설정한다.
- TimeBasedRollingPolicy를 사용하여 시간 기반으로 로그 파일을 롤링하고 있다.
- 날짜 기준으로 롤링된다.
왜 로그 파일을 남겨야 할까?
로그 파일을 저장하는 것은 운영 중 발생하는 오류와 성능 이슈를 분석하고 서비스 장애 발생 시 원인을 빠르게 추적하기 위해 필수적이다.
또한 보안 및 규정 준수를 위해 사용자 활동과 시스템 이벤트를 기록하여 감사 로그로 활용할 수도 있다.
즉 운영 환경에서 중요한 역할을 한다.
개발 환경별 로그 출력
<springProfile name="prod"> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </springProfile> <springProfile name="dev"> <root level="DEBUG"> <appender-ref ref="STDOUT"/> </root> <logger name="org.hibernate.type.descriptor.sql" additivity="false"> <level value = "TRACE" /> <appender-ref ref="STDOUT"/> </logger> </springProfile>
- 개발 중에는 보통 DEBUG 또는 INFO 수준을 사용한다.
- 위와 같이 프로덕션 환경에서는 최소한의 로그 수준을 설정하여 성능에 영향을 미치지 않도록 하는것이 일반적이다.
- 개발 환경에서는 쿼리문 로그에 출력되어 있는 파라미터에 바인딩 되는 값을 확인 시켜주기 위해 설정해준다.
마무리
XML 설정
<?xml version="1.0" encoding="UTF-8"?> <configuration> <property name="LOGS_ABSOLUTE_PATH" value="./logs"/> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %clr(%-5level) %cyan(%logger) - %msg%n</pattern> </encoder> </appender> <appender name="LOG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOGS_ABSOLUTE_PATH}/resource.log</file> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <charset>utf8</charset> <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread [%X{traceId}] %-5level %logger - %msg%n</Pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOGS_ABSOLUTE_PATH}/resource.%d{yyyy-MM-dd}.%i.gz</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>100MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> <maxHistory>180</maxHistory> </rollingPolicy> </appender> <springProfile name="prod"> <root level="INFO"> <appender-ref ref="STDOUT"/> <appender-ref ref="LOG"/> </root> </springProfile> <springProfile name="dev"> <root level="DEBUG"> <appender-ref ref="STDOUT"/> </root> <logger name="org.hibernate.type.descriptor.sql" additivity="false"> <level value="TRACE"/> <appender-ref ref="STDOUT"/> </logger> </springProfile> <!-- <logger name="com.nhnacademy.exam.service" level="DEBUG"/>--> </configuration>
Properties 설정
dev
# hibernate 로깅 logging.level.org.hibernate.type.descriptor.sql=TRACE # Log Levels logging.level.store.mybooks.resource = debug # Logging pattern logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %clr(%-5level) %cyan(%logger) - %msg%n # Logging file logging.file.name=${LOGS_ABSOLUTE_PATH}/resource.log logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %clr(%-5level) %cyan(%logger) - %msg%n logging.logback.rollingpolicy.max-file-size=100MB logging.logback.rollingpolicy.max-history=180
prod
# Log Levels logging.level.store.mybooks.resource = INFO # Logging pattern logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %clr(%-5level) %cyan(%logger) - %msg%n # Logging file logging.file.name=${LOGS_ABSOLUTE_PATH}/resource.log logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %clr(%-5level) %cyan(%logger) - %msg%n logging.logback.rollingpolicy.max-file-size=100MB logging.logback.rollingpolicy.max-history=180
'개발 ing' 카테고리의 다른 글
[My-Books] Logback 적용기 - 로그를 제대로 남겨보자! (0) 2025.03.11 [Agarang] 지금까지 내가 잘 못 알고 있었던 점 (2) - 아키텍처 재구성 (0) 2025.03.02 [DOSI:RAK] 부하테스트 시작 - CPU, 메모리, 디스크 (0) 2025.02.17 [Agarang] 지금까지 내가 잘 못 알고 있었던 점 (1) - 문제제기 (0) 2025.02.08 [DOSI:RAK] “DOSI:RAK 서비스 개발 : Spring EventListener로 객체지향 설계 문제 해결하기” (1) 2024.12.16