로그(Log)란?
로그(Log) 남기기
어플리케이션을 운영할 때 작동 정보인 로그(Log)를 기록하는 행위를 해주어야 합니다. 로그를 기록하면 어플리케이션의 상태를 추적하고, 오류 인지 및 잠재적인 문제를 진단할 수 있습니다. 즉 로깅을 통해 로직의 흐름을 파악함으로써 서비스의 품질을 관리할 수 있기 때문에 로깅은 개발자들에게 필수적입니다.
그러나 로그를 무분별하게 기록하면 로그 파일의 볼륨이 너무 커져 문제를 야기할 수 있습니다. 따라서 예외가 발생하는 곳이나, 중요 기능이 실행되는 부분에 적절한 로깅을 남겨 효율적으로 처리하는 것이 중요합니다.
로그 레벨
로그 레벨은 로그 메시지의 중요도를 나타냅니다. 로그 레벨에는 총 6가지의 레벨이 있습니다. 로깅 레벨을 올바르게 선택하면 적절한 정보만 로그로 남기고 로그 파일의 볼륨을 관리할 수 있습니다.
- TRACE : 가장 세분화된 정보로 개발 단계에서 사용합니다.
- DEBUG : 디버깅할 정도로 세분화된 정보를 제공합니다. SQL 로깅을 할 수 있으며 역시 개발 단계에서 사용합니다.
- INFO : 어플리케이션 운영 상황 정보를 제공합니다.
- WARN : 프로그램 실행에는 문제가 없지만 잠재적인 오류가 있다는 경고 상태입니다.
- ERROR : 요청 처리 중 오류가 발생한 상태입니다.
- FATAL : 아주 심각한 오류가 발생한 상태입니다.
로그는 선택한 레벨 부터 높은 레벨의 로그까지 찍힙니다.
위 코드에서 낮은 레벨인 TRACE부터 높은 레벨인 ERROR까지 로그를 남겨보려 했을 때, 위 결과와 같이 INFO부터 ERROR까지만 로그가 찍힌 것을 확인할 수 있습니다. 그 이유는 기본 로그 레벨이 INFO로 설정 되어있기 때문입니다.
로그 레벨을 변경하는 것도 가능한데, 이는 application.properties(yml) 파일에서 변경이 가능합니다.
아래와 같이 root로 전체 로깅 레벨을 설정할 수 있고, 패키지 별로 로그 레벨 설정이 가능합니다. 로그 레벨의 우선 순위는 안 쪽에 있을수록 높습니다.
아래는 com.dutmdcjf.test.controller 내에 있는 Controller들의 로그 레벨을 trace로 설정한 예제입니다. TestController의 로그 레벨이 trace가 됨을 확인할 수 있습니다.
logging:
level:
root: info
com.dutmdcjf.test: debug
com.dutmdcjf.test.controller: trace
로그 남기는 방법
Spring Boot에서 spring-boot-starter-logging은 spring-boot-starter-web안에 포함되어 있기 때문에 다른 의존을 추가하지 않고 사용할 수 있습니다. 로그를 사용하는 법은 두 가지 방식이 있는데 두 방식은 위 예제에서 사용했습니다 :)
1) LoggerFactory
LoggerFactory에서 가져오는 방식입니다. 해당 코드에서는 static을 선언하지 않았지만, 실제로 사용할 때는 static을 사용해주어야합니다. Logger 인스턴스를 여러 번 만들 필요가 없습니다. 때문에 Logge를 static으로 선언함으로써 맨 처음 클래스 로딩 시 한 번만 생성되고, 메모리 사용량과 객체 생성 비용이 절감됩니다.
2) @Slf4j
롬복(Lombok)에서 제공하는 어노테이션 중 하나로, 이 어노테이션을 사용하면 Logger 객체를 간편하게 생성할 수 있습니다.
Logback
로그백(Logback)은 SLF4J의 구현체 중 하나입니다. SLF4J를 통해 로그 메시지를 기록하는 경우, 이 로그 메시지는 로그백을 통해 처리됩니다.
즉, @Slf4j를 통해 Logger 객체를 간편하게 생성하고, 이렇게 생성된 Logger 객체는 SLF4J의 인터페이스를 구현한 로깅 시스템(Logback)을 사용하여 로그를 기록합니다.
Logback 설정 파일 읽는 순서
스프링 부트에서 로깅 관련 설정을 읽어 들이는 순서는 다음과 같습니다.
1.resources 디렉터리 아래에 logback-spring.xml 또는 logback.xml 파일이 있으면 해당 파일의 설정을 읽습니다.
2. logback-spring.xml 파일이 없는 경우 .properties(.yml) 파일을 읽습니다.
3. 위 두 파일이 모두 있는 경우 logback-spring.xml을 적용시키고, .properties 파일 설정을 덮습니다.
💡logback-spring.xml과 yml 설정 파일
Logback 설정 파일을 생성할 때 logback-spring.xml과 yml파일 중 무엇을 사용하는 것이 좋을까요?
1. XML 파일:
Logback에서 기본적으로 사용되는 설정 형식입니다. XML 파일을 통해 다양한 설정을 유연하고 구체적인 설정을 할 수 있습니다. XML 파일은 문법이 복잡하고 가독성이 떨어진다는 단점이 있습니다.
2. YAML 파일:
YAML 파일은 XML에 비해 문법이 간결하고 가독성이 좋아 설정 파일을 작성하기 편리합니다. 그러나 XML 파일보다는 설정 옵션이나 구성이 제한적일 수 있다는 단점이 있습니다.
너무 복잡하지 않은 간단한 설정인 경우 YAML 파일을 써도 아무런 문제가 없습니다. 더 세세한 커스텀들을 하고 싶다면 XML파일로 작성하는 것이 유리합니다.
아래 설명들은 Logback에서 기본적으로 사용되는 설정 방식인 XML에 대해서 설명하고, YAML 파일 형식으로 설정하는 코드는 가볍게만 작성하도록 하겠습니다.
Logback 설정 파일 작성 시작
`logback-spring.xml` 파일을 생성하고 다음과 같이 설정을 시작합니다.
경로: src/main/resources/logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 이곳에 설정을 작성 -->
</configuration>
- scan:
설정 파일의 변경을 감지할지 여부를 지정합니다. 기본값은 false이고, true인 경우 Logback은 설정 파일의 변경을 주기적으로 검사하고 자동으로 로드합니다. scan만 true로 한 경우 설정 파일의 변경을 60초(기본값)마다 검사합니다. - scanPeriod:
설정 파일의 변경을 감지할 주기를 지정합니다. scan만 true로 한 경우 기본값은 `60 seconds`입니다.
appender : 로그를 출력 방식 지정
Logback에서 appender는 로그 이벤트가 어디로 출력되는지를 정의합니다. 로그 이벤트는 콘솔, 파일, 데이터베이스 등 다양한 대상으로 출력될 수 있습니다.
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
...
</appender>
- name:
`appender`에 이름 부여하여 `appender`를 참조할 때 사용됩니다. - class:
특정한 로그 메시지를 처리할 방식을 지정할 수 있습니다.
Logback에서 사용할 수 있는 appender class 들은 다음과 같습니다.
- ConsoleAppender: 로그를 콘솔에 출력하는 appender
- FileAppender: 로그를 파일에 출력하는 appender
- RollingFileAppender: 일정 크기나 시간 단위로 로그 파일을 롤링하여 관리하는 appender
- SMTPAppender: 로그를 이메일로 보내는 appender
- SocketAppender: 로그를 TCP 또는 UDP 소켓으로 전송하는 appender
- DBAppender: 로그를 데이터베이스에 저장하는 appender
콘솔 출력:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="1 hour">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<root level="warn">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4. 로그 기록 방식
appender의 class를 지정하여 로그 메시지를 처리할 방식을 지정해주었습니다.
1) 콘솔 출력
<!-- 콘솔 출력을 위한 ConsoleAppender 설정 -->
<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>
2) 파일 출력
<!-- 파일 출력을 위한 FileAppender 설정 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- 파일 저장 경로 -->
<file>my.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
3) 일별로 로그 파일을 생성하고 크기가 일정 이상이 되면 압축하여 보관
<!-- RollingFileAppender 설정 -->
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>myapp.log</file>
<!-- 파일 롤링 정책 설정 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 로그 파일의 최대 크기 -->
<maxFileSize>10MB</maxFileSize>
<!-- 로그 파일의 유지 기간 -->
<maxHistory>7</maxHistory>
<!-- 로그 파일의 이름 패턴 -->
<fileNamePattern>myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
- rollingPolicy
- FixedWindowRollingPolicy
: 일정 수의 로그 파일을 보존하고, 파일이 많아지면 오래된 것은 삭제하고 새로운 파일로 롤링합니다. - SizeAndTimeBasedRollingPolicy
: 크기와 시간 기반으로 로그파일을 롤링합니다. 크기나 시간 중 하나의 조건만 충족되면 롤링이 발생합니다. - TimeBasedRollingPolicy
: 시간 기반으로 로그 파일을 롤링합니다.
- FixedWindowRollingPolicy
<!-- FixedWindowRollingPolicy -->
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<!-- 보존할 이전 로그 파일의 최대 개수 -->
<maxIndex>5</maxIndex>
<!-- 로그 파일의 이름 패턴 -->
<fileNamePattern>myapp.%i.log</fileNamePattern>
</rollingPolicy>
<!-- SizeAndTimeBasedRollingPolicy -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 로그 파일의 최대 크기 -->
<maxFileSize>10MB</maxFileSize>
<!-- 로그 파일의 유지 기간 -->
<maxHistory>7</maxHistory>
<!-- 로그 파일의 이름 패턴 -->
<fileNamePattern>myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
</rollingPolicy>
<!-- TimeBasedRollingPolicy -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 로그 파일의 이름 패턴 -->
<fileNamePattern>myapp.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
</rollingPolicy>
appender 내 다른 설정
<property name="LOGS_PATH" value="./logs"/>
<appender name="DAILY_ROLLING_FILE_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 파일 저장 경로 -->
<file>${LOGS_PATH}/api.log</file>
<!-- 필터를 추가하여특정 레벨만 기록할 수 있습니다. -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch> <!-- 해당 레벨만 기록한다. -->
<onMismatch>DENY</onMismatch> <!-- 다른 수준의 레벨은 기록하지 않는다.(상위 레벨도 기록 안함), 상위 수준의 레벨에 대한 기록을 원하면 ACCEPT 로 하면 기록된다. -->
</filter>
<!-- 로그 메시지 pattern 지정 -->
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern> <!-- 해당 패턴 네이밍으로 현재 로그가 기록됨 -->
</encoder>
</appender>
5. 로그 레벨 설정
루트 로거(root logger)에 대한 로그 레벨을 설정하고, 어떤 `Appender`를 사용하여 출력할 것인지 지정합니다. 아래 예제는 INFO 레벨 이상의 로그를 콘솔에 출력합니다.
<root level="INFO">
<appender-ref ref="STDOUT"/>
<!-- 다른 Appender도 여기에추가 가능
<appender-ref ref="DAILY_ROLLING_FILE_APPENDER" />
<appender-ref ref="ERROR_FILE_APPENDER" />
-->
</root>
6. 클래스 레벨 로그 설정
특정 클래스에서 발생하는 로그를 설정하려면 `logger` 설정을 해주면 됩니다. 해당 클래스의 이름 또는 패키지를 설정해 주면 지정한 파일들로 로그가 설정됩니다. 아래 예제는 `LogController` 클래스에서 발생하는 DEBUG 레벨 이상의 로그를 설정한 예입니다.
<!-- 콘솔에 출력된 LogController에 대해 실행 -->
<logger name="LogController" additivity="false">
<!-- DEBUG 레벨 이상에서만 실행 -->
<level value = "DEBUG" />
<appender-ref ref="DAILY_ROLLING_FILE_APPENDER" />
<appender-ref ref="ERROR_FILE_APPENDER" />
</logger>
아래 설정은 Hibernate의 SQL의 쿼리를 디버그 하기 위해 `org.hibernate.SQL` 패키지에서 발생하는 DEBUG 레벨 이상의 로그를 설정한 예입니다.
<logger name="org.hibernate.SQL" additivity="false">
<!-- DEBUG 레벨 이상에서만 실행 -->
<level value = "DEBUG" />
<appender-ref ref="hibernate_SQL_DEBUG_LOG" />
</logger>
7. 콘솔 내 로그 패턴 및 색상 처리
로그 패턴을 지정하여 로그 메시지의 형식을 지정할 수 있습니다. 또한 Logback을 사용하여 로그 메시지에 색상을 지정할 수도 있습니다.
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
</layout>
</appender>
- conversionWord를 "clr"로 정의하였습니다.
- 아래처럼 로그 패턴을 정의할 때 정의해 주면 됩니다.
- %clr(출력 내용){color}
- color 부분에 색을 지정하면 됩니다.
- <Pattern> 태그에 바로 지정하려 하였으나 너무 길어져서 <property> 태그로 따로 정의하였습니다.
'Spring Boot' 카테고리의 다른 글
Spring Boot Validation @NotNull, @NotEmpty, @NotBlank 차이점 (0) | 2023.10.24 |
---|---|
SpringBoot에서 JUnit5로 효율적인 단위 테스트 작성하기, Assertions로 값 검증하기 (0) | 2023.10.04 |
SpringBoot에서 MockMvc을 활용한 컨트롤러, HTTP 요청 테스트 방법 (0) | 2023.10.04 |
Spring Scheduler를 활용한 일정 주기 스케줄링 작업 (0) | 2023.01.25 |
Spring Boot Hibernate Validator와 Data Binding: 데이터 유효성 검사와 데이터 연결 (0) | 2022.08.31 |