로깅(logging), 어떻게 사용해야 할까?

junto·2024년 2월 9일
0

spring

목록 보기
3/30
post-thumbnail

로깅

  • 로그를 사용하는 이유는 애플리케이션 동작을 감시하여 문제가 발생했을 때 신속하게 대응하기 위해서이다.

System.out.println()을 사용하면 되지, 꼭 로깅할 필요가 있을까?

  • 로깅 라이브러리는 유연성, 성능, 유지보수 측면에서 큰 이점을 제공한다.
    • 로그 레벨(trace, debug, info, warn, error)등으로 유연하게 정보를 로깅할 수 있다.
    • 로깅은 내부 버퍼를 사용하여 비동기적으로 로깅할 수 있어 성능상 이점이 있다. (별도의 스레드 사용, I/O 지연 처리)
    • 로깅 관련 설정(로그 레벨, 포멧, 출력 대상)을 파일 하나로 관리할 수 있으며 필요에 따라 콘솔, 파일, 네트워크에 리다이렉션 할 수 있다.

로깅 사용

  1. 로거 인스턴스 사용
private static final Logger log = LoggerFactory.getLogger(LoggingController.class);
  1. @Slf4j 롬복 어노테이션 사용
@Slf4j
@Service
public class ContactService {}

로깅 레벨

  • TRACE < DEBUG < INFO < WARN < ERROR
    • 왼쪽으로 갈수록 상세한 수준의 로그로 다음 레벨의 내용을 포함한다.
    • TRACE 로그의 경우 DEBUG, INFO, WARN, ERROR 로그를 모두 포함한다.
  • ERROR: 즉시 대응해야 할 에러 로그
  • WARN: 상황에 따라 잠재적으로 위험할 수 있을 때 또는 예외 처리
  • INFO: 로그에 저장하고 싶은 중요 애플리케이션 이벤트
  • DEBUG: 개발 단계에서 사용. 디버깅이 가능하도록 상세한 내용 출력
  • TRACE: 개발 단계에서 사용. 모든 레벨 로그가 출력

SpringBoot 기본 로깅

  • 기본적인 로깅 수준은 INFO로 사전 설정되어 있다. application.yml에서 패키지, 클래스 별로 어느 정도의 정보를 로그로 남길지 정할 수 있다.
logging:
  level:
    root: warn # 애플리케이션 전반 로그 레벨 warn
    org.springframework.web: debug # Spring Web 로그 레벨 debug
    org.hibernate: error # Hibernate 로그 레벨 error
    com.core.book.BookServiceController: trace # BookServiceController 로그 레벨 trace
  • 설정을 변경하지 않고 로깅 수준을 변경하려면 jar로 컴파일 시에 -debug, -trace 인수를 전달하면 된다.
java -jar target/spring-boot-logging-0.0.1-SNAPSHOT.jar --trace
  • 추가로 VM 옵션에서 로깅 수준을 설정하거나 Maven 또는 Gradle로 빌드할 때 로그 설정을 인수로 전달할 수 있다.

SLF4J(Simple Logging Facade for Java)

  • SLF4J는 여러 로깅 프레임워크(logback, log4j, java.util.logging)에 대한 일관된 인터페이스를 제공하는 로깅 API의 집합이다.
  • 로깅 구현체와의 결합을 최소화하여 로깅 구현체의 내부 구조나 동작 방식을 알 필요 없이 로깅 기능을 사용할 수 있다.
  • spring-boot-starter 의존성이 추가되어 있으면 logback 로그 프레임워크를 사용할 수 있다.
// 로거 인스턴스 또는 @Slf4j 롬복 어노테이션 사용
public void logs() {
	log.error("error message");
    log.warn("warn message");
    log.info(info message");
    log.debug("debug message");
    log.trace("trace message");
}

로깅 성능

  • 아래 코드는 동일한 출력을 가진다.
logger.debug("The new entry is "+entry+"."); logger.debug("The new entry is {}.", entry);

logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);
  • 공식문서에서 아래 코드가 대략 30배 정도 더 빠르다고 한다. 그 이유를 살펴보면,
    1. 지연 문자열 평가(Lazy String Evaluation)
      • 전자는 로깅 레벨에 상관없이 모든 문자열 연산이 수행되지만, 후자는 로깅 레벨을 만족해야 문자열 연산이 수행된다.
    2. 효율적인 문자열 처리
      • 전자는 문자열 연산마다 새로운 문자열을 만들고 이를 복사하는 연산이 추가로 들어가지만, 후자는 필요한 최종 문자열의 형태가 만들어지고 실제 값이 로깅 시에만 삽입된다고 한다.

Logback 설정

  • Logback이란 로깅 구현체로 실제 로깅을 수행하는 역할을 하며 성능 향상과 유연한 구성 옵션을 제공한다.
  • 다음 이름 중 하나에 해당하는 파일이 있으면 해당 파일로 세부 설정을 할 수 있다.
    • logback-spring.xml, logback.xml, logback-spring.grooby, logback.groovy
    • 여기서 세부 설정이란 다양한 색상, 출력 형식, 로깅 이름, 로깅 레벨, 스레드 이름, 콘솔과 파일 출력, 대량 로그 파일 생성 방지를 위한 롤링 정책 등을 말한다.
  • 기본 구조
    • property
    • appender
    • encoder
    • pattern
    • root
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  
    <!-- 콘솔 출력을 위한 Appender -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} - %level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

  	<!-- 파일 출력을 위한 Appender -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/myapp.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/archived/myapp-%d{yyyy-MM-dd}.log.zip</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

  	<!-- 로그 레벨과 Appender 설정 -->
    <root level="info">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
  
	<!-- 특정 패키지 또는 클래스의 로그 레벨을 조정 -->
    <logger name="org.springframework.web" level="debug"/>
    <logger name="org.hibernate" level="error"/>
</configuration>
  • appender는 어디에 로그를 출력할 것인가를 설정한다.
    • 위의 예시에선 로그 메시지를 콘솔(STDOUT) 또는 파일(FILE)에 출력한다. 각각 ConsoleAppender(콘솔 저장), RollingFileAppender(어러 파일 순회하며 저장)를 사용한다.
    • 이외에도 FileAppender(파일 저장), SMTPAppender(메일 저장), DBAppender(DB 저장) 구현체가 존재한다.
  • encoder는 로그 메시지의 형식을 지정한다.
  • TimeBasedRollingPolicy는 로그 파일을 날짜별로 롤링하여 저장한다.
    • logs/archived/myapp-%d{yyyy-MM-dd}.log.zip 날짜별 롤링
    • logs/archived/myapp-%d{yyyy-MM-dd-HH-mm}.log.zip 분별 롤링
    • 아래처럼 로그파일이 롤링된다.

RollingFileAppender 옵션

  • 롤링 전략(Rolling Policy): 롤링이 언제 발생할지를 결정
    • TimeBasedRollingPolicy: 시간(예: 매일, 매시간)에 따라 파일을 롤링
    • SizeBasedTriggeringPolicy: 파일 크기가 특정 기준을 초과했을 때 롤링
      - FixedWindowRollingPolicy: 고정된 수의 롤링 파일을 유지하며, 새 파일이 추가될 때 가장 오래된 파일을 삭제
  • Triggering Policy(트리거 정책): 롤링이 발생하는 조건을 정의
    • SizeBasedTriggeringPolicy: 파일 크기에 따라 롤링을 시작
      - TimeBasedTriggeringPolicy: 시간 간격에 따라 롤링을 시작
  • MaxFileSize(최대 파일 크기): 한 로그 파일의 최대 크기를 설정. 이 크기를 초과하면 새로운 로그 파일이 생성
  • MaxHistory(최대 기록): 보관할 로그 파일의 최대 수를 설정. 이 수를 초과하는 오래된 파일은 자동으로 삭제
  • TotalSizeCap(총 크기 제한): 모든 로그 파일의 총 크기 제한을 설정. 이 제한을 초과하면 오래된 파일부터 삭제
  • append: 로그 파일이 이미 존재할 때, 새로운 로그를 파일 끝에 추가할지 여부를 결정. 기본값은 true

참고자료

  • elice
  • baeldung, logging
  • logback, docs

    #코딩독학 #코딩인강 #코딩배우기 #개발자 #코딩이란 #코딩교육
    #프론트엔드부트캠프 #백엔드부트캠프 #국비지원부트캠프 #개발자 #백엔드 #AI부트캠프 #개발자국비지원 #백엔드개발자 #프론트엔드개발자
profile
꾸준하게

0개의 댓글