[번역자료] spring boot Logback(로그)

2021. 9. 17. 02:17개발공부/SpringBoot

spring Logback

spring-boot-starter-logging은 간단한 설정으로 편리한 logging을 제공합니다. 설정은 application.properties, XML 두 가지 방식이 있으며
복잡한 커스텀이 필요하다면 XML을 사용해야지만 됩니다.

spring-boot-starter 내에는 logback-classic, slf4j 의존성이 포함되어 있습니다.

작동원리를 이해하기 좋은 방법은 직접 사용하면서 연습해보는 것 같습니다.
Logger 예제코드를 위해 Service 클래스를 하나 생성해줍니다.

@Service
public class LoggerService {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggerService.class);

    public void doStuff(final String value) {
        LOGGER.trace("doStuff needed more information - {}", value);
        LOGGER.debug("doStuff needed to debug - {}", value);
        LOGGER.info("doStuff took input - {}", value);
        LOGGER.warn("doStuff needed to warn - {}", value);
        LOGGER.error("doStuff encountered an error with value - {}", value);
    }
}

그리고 첫 시작으로 간단한 코드를 logback.xml에 작성해주면 Logback 파일이 설정을 위해 찾아 세팅해줄 것입니다.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS} $magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n
</pattern>
</encoder>
</appender>

<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

ConsoleAppenderSystem.out.println처럼 콘솔에 log를 출력해주는 appender 클래스 입니다. 특히 pattern에 여러 %가 붙어진 표기들이 있는데
생성되는 값으로 대체될 부분이라 보면됩니다.

  • %d - 로그 메시지가 발생했을 때 시간을 SimpleDateFormat으로 출력할 수 있도록 해줍니다.
  • %thread - 로그 메시지가 발생한 스레드명을 출력해줍니다.
  • $-5level - 로그 메시지의 로깅 레벨을 출력합니다.
  • %logger{36} - 로그 메시지가 발생한 패키지 + 클래스명을 출력합니다. 괄호 안의 숫자는 글자 수 max length를 표기합니다.
  • %M - 로그 메시지가 발생한 메서드명을 출력합니다.
  • %msg - 로그 메시지를 출력합니다.
  • %n - 라인 변경
  • %magenta() - 괄호 안의 텍스트 색을 설정합니다(magenta). 다른 색으로도 변경가능합니다.
  • highlight - 괄호 안의 텍스트를 강조합니다.

설정한 appender는 root logger에 참조됩니다. 상기 코드는 INFO(대소문자 구분없음)로 설정되어있는 상태입니다.
Logback애서 가능한 로깅레벨은 다음과 같습니다.

  • OFF (output no logs)
  • ERROR
  • WARN
  • INFO
  • DEBUG
  • TRACE
021-09-17 00:19:36.242 [main] INFO  com.jpa.shop.logger.LoggerService.doStuff - doStuff took input - 테스트중:INFO
2021-09-17 00:19:36.243 [main] WARN  com.jpa.shop.logger.LoggerService.doStuff - doStuff needed to warn - 테스트중:INFO
2021-09-17 00:19:36.243 [main] ERROR com.jpa.shop.logger.LoggerService.doStuff - doStuff encountered an error with value - 테스트중:INFO

테스트 결과 INFO 로깅 레벨 이상의 로그들만 출력되었음을 볼 수 있습니다. DEBUG, TRACE 레벨의 메시지는 출력되지 않았습니다.

properties(yaml)로 설정하고 싶다면 아래와 같이 설정해주면 됩니다. 훨씬 간결한 코드로 XML 설정파일을 대체할 수 있습니다.

logging:
  level:
    root: info
  pattern:
    console: %d{yyyy-MM-dd HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n

로그 메시지를 특정 클래스에서 root level이 아닌 고유한 설정으로 로그 레벨을 지정해주고 싶다면 아래 코드와 같이 설정해주면 됩니다.

    <logger name="com.jpa.shop.logger.LoggerService" level="debug">
        <appender-ref ref="STDOUT"/>
    </logger>
//결과값
2021-09-17 00:33:38.815 [main] DEBUG com.jpa.shop.logger.LoggerService.doStuff - doStuff needed to debug - 테스트중:INFO
2021-09-17 00:33:38.815 [main] DEBUG com.jpa.shop.logger.LoggerService.doStuff - doStuff needed to debug - 테스트중:INFO
2021-09-17 00:33:38.816 [main] INFO  com.jpa.shop.logger.LoggerService.doStuff - doStuff took input - 테스트중:INFO
2021-09-17 00:33:38.816 [main] INFO  com.jpa.shop.logger.LoggerService.doStuff - doStuff took input - 테스트중:INFO
2021-09-17 00:33:38.816 [main] WARN  com.jpa.shop.logger.LoggerService.doStuff - doStuff needed to warn - 테스트중:INFO
2021-09-17 00:33:38.816 [main] WARN  com.jpa.shop.logger.LoggerService.doStuff - doStuff needed to warn - 테스트중:INFO
2021-09-17 00:33:38.817 [main] ERROR com.jpa.shop.logger.LoggerService.doStuff - doStuff encountered an error with value - 테스트중:INFO
2021-09-17 00:33:38.817 [main] ERROR com.jpa.shop.logger.LoggerService.doStuff - doStuff encountered an error with value - 테스트중:INFO

변경된 설정에 따라 로그 레벨이 DEBUG 이상으로 찍힌 것을 확인할 수 있습니다. 하지만 로그가 중복 출력된 것은 바로 잡아야 합니다. 이를 위해 additivity=false
설정해줘야합니다. 또는 출력이 되지 않게끔 appender를 생략해주면 됩니다.

    <logger name="com.jpa.shop.logger.LoggerService" additivity="false" level="debug">
        <appender-ref ref="STDOUT"/>
    </logger>
///--------
    <logger name="com.jpa.shop.logger.LoggerService" level="debug"/>

이또한 properties(yaml) 설정으로 대체가능합니다.

logging:
  level:
    com:
      jpa:
        shop:
          logger:
            LoggerService: debug

물론 클래스 대신 패키지 뎁스로 설정해줄수도 있습니다. 효과적인 예시로는

<logger name="org.springframework.boot" level="debug">
  <appender-ref ref="STDOUT" />
</logger>

<logger name="org.springframework.boot.SpringApplication" level="debug">
  <appender-ref ref="STDOUT" />
</logger>

두 가지 예시가 있습니다. 한 뎁스 차이지만 로그 출력 라인은 약 100개 가량의 차이가 납니다.
물론 이또한 properties(yaml)로 설정이 가능합니다.

Properties는 재사용이 필요한 데이터를 변수화할 수 있는 태그입니다. 출력한 로그를 파일로 만드는 경로에 사용됩니다.

    <property name="LOG_PATH" value="logs"/>
//properties(yaml)
        logging:
            path:logs

LOG_PATH는 이후 예제에서 사용될 것이며 HOME/logs 디렉토리를 사용할 거란 뜻입니다 HOME은 제 프로젝트의 root 디렉토리입니다. 이 디렉토리는
저장하기 적합한 곳은 아니지만 연습예제에선 문제없습니다. XML파일 내에서 LOG_PATH값에 접근하려면 ${LOG_PATH} 형식으로 추가해줘야합니다.

이런 형태로 Spring boot 애플리케이션 내에서 property값을 설정해두고 ${property} 형식으로 사용할 수 있습니다.

출력된 로그를 파일로 저장하려면 FileAppender를 사용해야합니다. 하지만 이는 단순히 모든 로그를 한 파일에 저장하기 때문에 용량이 커질 위험이 있습니다.
그래서 RollingFileAppender 사용을 권장합니다.

    <appender name="SAVE-TO-FILE" class="ch.qos.logback.core.FileAppender">
        <file>${LOG_PATH}/log.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
            </Pattern>
        </encoder>
    </appender>

사실 ConsoleAppender와 크게 다르지 않습니다. encoder pattern에 글자 색이나 강조 설정만 제거했을 뿐입니다. properties(yaml)을 사용하려면
아래 설정을 추가해줘야 합니다.

logging.pattern.console=
logging.path=logs
logging.file=${logging.path}/log.log
logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n

XML 설정과 차이는 LoggerService만 출력 파일에 담았던 XML과 달리 properteis(yaml)는 root logger까지 모두 포함해 저장하게 됩니다.

RollingFileAppender는 rolling policy에 따라 로그들을 별도의 파일들로 나뉘어 저장합니다. 예를 들어 파일 사이즈나 기간에 따라 말이죠.
이는 하나의 파일안에서 방대한 히스토리를 들춰보지 않아도 된다는 것을 뜻합니다.

TimeBasedRollingPolicy는 새로운 파일을 date 기준으로 생성합니다. 아래 코드는 date를 로그파일 이름에 %d를 사용해 붙여줍니다. %d 표기는
rollover time 기간을 참조하기 때문에 중요하다고 볼 수 있습니다. 아래 코드는 날마다 파일이 업데이트 되도록 설정되었지만 월마다로 설정을 변경하고 싶으면
%d{yyyy-MM}로 바꿔주면 됩니다.

    <appender name="SAVE-TO-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/log.log</file>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
            </Pattern>
        </encoder>

        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>
                ${LOG_PATH}/archived/log_%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <maxHistory>10</maxHistory>
            <totalSizeCap>100MB</totalSizeCap>
        </rollingPolicy>
    </appender>

추가적으로 TimeBaseRollingPolicymaxHistory는 archived 로그 파일들이 얼마만큼 보관될지 설정하는 것입니다. totalSizeCap
모든 archived된 파일들의 크기를 말합니다. 삭제될 때 우선순위는 maxHistory가 앞서 가집니다.

이 설정은 properties(yaml)의 권한범위 밖입니다. 디폴트는 maxHistory:7에 rolloversize:10MB 입니다.

이전 예시의 경우 rollover 될 때 archive 폴더에 저장됐지만 FixedWindowRollingPolicy와 트리거 policy SizeBasedTriggeringPolicy의 경우
용량이 적기 때문에 곧바로 log 디렉토리에 저장했습니다.

minIndexmaxIndex%i의 최소, 최대값을 말합니다. 그래서 log.log 파일이 maxFileSize를 넘기면 log_2.log 파일이 archived 폴더에
저장되며 새로운 log.log 파일이 생성됩니다. 새로운 파일이 반복해서 maxFileSize를 초과하면 새로운 log.log파일이 생기고 기존 파일은 log_3.log파일로
archived 폴더에 저장되는 과정이 반복해서 일어납니다. 이 과정은 maxIndex에 이르기 까지 계속되며 기존 파일은 삭제되고 새로운 archived 폴더에
저장되는 파일은 이름이 변경돼 저장됩니다.

말로 설명해 복잡할 수 있으니 단계적으로 나열해보겠습니다.

  • log.log가 최대 크기를 초과합니다. -> log.loglog_2.log로 이름이 바뀌며 새로운 log.log가 생성됩니다.
  • log_2.log가 최대 크기를 초과합니다. -> log_2.loglog_3.log로 이름이 바뀌며 log.loglog_2.log로 변경됩니다. 그리고 새로운 log.log가 생성됩니다.
  • log_3.log가 최대 크기를 초과합니다. -> log_3.log는 삭제됩니다. log_2.loglog_3.log로 이름의 변경되며, log.loglog_2.log로 저장되며 새로운 log.log가 생성됩니다.

SizeAndTimeBasedRollingPolicy는 파일 크기와 기한 두 개의 조건 모두 가지고 있는 policy입니다. 따라서 파일에 %d%i가 함께 들어갑니다.

        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>
                ${LOG_PATH}/archived/log_%d{yyyy-MM-dd}_%i.log
            </fileNamePattern>
            <maxFileSize>10MB</maxFileSize>
            <maxHistory>10</maxHistory>
            <totalSizeCap>100MB</totalSizeCap>
        </rollingPolicy>

아래 코드는 console과 file에 적용되는 multiple appender 입니다. logger에 multipleappender를 추가하면 하나로 묶어 설정해줄 수 있습니다.

    <logger name="com.jpa.shop.logger.LoggerService" additivity="false" level="debug">
        <appender-ref ref="SAVE-TO-FILE"/>
        <appender-ref ref="STDOUT"/>
    </logger>

Spring Boot는 환경에 따라 다른 Logback 설정을 적용할 수 있도록 제공합니다. 따라서 개발환경일 때 console과 file 저장을 둘 다 설정하고
서비스 환경일 땐 file 저장만 하고 싶다면 설정을 통해 쉽게 가능합니다.

    <springProfile name="dev">
        <root level="info">
            <appender-ref ref="STDOUT"/>
        </root>
    <logger name="com.jpa.shop.logger.LoggerService" additivity="false" level="debug">
        <appender-ref ref="SAVE-TO-FILE"/>
        <appender-ref ref="STDOUT"/>
    </logger>
    </springProfile>

    <springProfile name="prod">
        <root level="info">
            <appender-ref ref="SAVE-TO-FILE"/>
        </root>
        <logger name="com.jpa.shop.logger.LoggerService" additivity="false" level="debug">
            <appender-ref ref="SAVE-TO-FILE"/>
        </logger>
    </springProfile>

이를 위해선 logback.xml 파일을 logback-spring.xml로 이름변경해줘야 합니다. springProfile을 사용하기 위해선 말이죠.


출처: lankydan.dav-configuring-logback-with-spring-boot