본문 바로가기

개발 이야기/Spring Boot

[10분 Boot-up] Kotlin으로 스프링부트 Web Application 만들기 - (3) Logback 설정하기

 

Kotiln과 Spring Boot을 모르더라도 10분씩 따라하면서 자연스럽게 Web Application을 만들어보는 것이 목표입니다.

각 챕터에서 나오는 중요한 내용은 별도로 [더 알아보기]에 보충해서 작성할 예정입니다.

 

시작하기 전에

Web Application을 운영하다보면 Application 자체의 Matrix 정보도 서비스 모니터링에 매우 중요한 요소지만 Logging은 비지니스 로직의 동작과 오류를 자세하게 추적할 수 있는 또 하나의 강력한 요소라고 생각한다. 특히 Error가 발생한 상황에서 남긴 적절한 Logging은 어느 부분에서, 어떻게 문제가 발생했는지 추적할 수 있는 가장 중요한 첫번째 단서가 된다.

 

Logging을 수행하기 위한 Logger로서 Java 생태계에서는 Log4j(Log4j 2), Logback 2가지가 대표적으로 사용되고, 이중에서 Logback은 Spring Boot에서 SLF4J(simple logging facade for java) 인터페이스 기반의 구현체로 사용한다.

 

spring-boot-starter의 dependency tree를 확인해보면 spring-boot-starter-logging이 포함되어 있고, 그 안에 다시 slf4jlogback-classic이 자동으로 포함되어 있다. 따라서 별도 dependency를 추가하지 않아도 Logger를 사용할 수 있지만, Kotlin 기반의 Spring Boot 어플리케이션에서는 SLF4J를 wrapping한 kotlin-logging이라는 경량 프레임워크를 통해서 조금 더 쉽고 효율적으로 Logger를 사용할 수 있다. (예를들면 Logger 자체의 선언도 쉽고, lamda 안의 메시지는 로그 레벨을 확인하기 전에 String 객체 생성을 막을 수 있다.)

kotlin-logging dependency 추가

/build.gradle.kts 파일에 kotlin-logging을 dependency에 추가한다.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("io.github.microutils:kotlin-logging:1.12.5") // Logging

    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

이제 MyappApplication.kt에 아래와 같이 logging 코드를 추가해서 Application을 실행해보자.

package me.sample.myapp

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

private val logger = KotlinLogging.logger {}

@SpringBootApplication
class MyappApplication {

    @Value("\${my-app.welcome-message}")
    val myAppWelcomeMessage: String = ""

    @PostConstruct
    fun printMyAppWelcomeMessage() {
        println(myAppWelcomeMessage)
    }
}

fun main(args: Array<String>) {
    runApplication<MyappApplication>(*args)

    // Logging
    logger.trace { "I'm trace! hello, world." }
    logger.debug { "I'm debug! Hello world." }
    logger.info { "I'm info! Hello world." }
    logger.warn { "I'm warn! Hello world." }
    logger.error { "I'm error! Hello world." }
}

아래처럼 INFO 레벨부터 콘솔에 출력되는 것을 확인할 수 있다. 즉, 별다른 설정이 없어도 기본적으로 콘솔 Logging이 가능하며 Log의 default 레벨은 INFO인 것을 예상할 수 있다. SLF4J에서는 Log 레벨은 총 5가지로 TRACE, DEBUG, INFO, WARN, ERROR가 있다.

 

Logback 설정

Dependency를 추가했고 기본적인 동작을 확인했으니 Logback 설정을 따로 추가한다. 추가로 Logback 설정을 하는 이유는 Logging을 console 뿐만 아니라 다양한 appender를 통해서 file에 남기거나 외부로 전송하는 등의 설정이 필요할 수 있고, profile에 따라서 각각의 appender 적용여부를 적절하게 분기하기 위해서다.

 

Spring Boot는 기본적으로 /resource 디렉토리 하위에 logback-spring.xml 파일을 Logback의 설정 파일로 인식한다. logback-spring.xml 파일을 생성하고 아래처럼 console-logging file-logging라는 profile로 분리한다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>

<configuration>
  <springProfile name="console-logging">
    <include resource="logback/logback-console.xml" />
  </springProfile>
  <springProfile name="file-logging">
    <include resource="logback/logback-file.xml" />
  </springProfile>
</configuration>

만약 Profile이 console-logging을 포함한다면 logback/logback-console.xml 설정이 적용되고, file-logging을 포함한다면 logback/logback-file.xml 설정이 적용될 것이다. Logback 설정 자체는 처음하는 사람 입장에서는 XML 안의 내용은 조금 복잡할 수 있으니 일단 예제와 똑같이 사용하고, 공식 문서를 참고하면서 설정해야한다.

logback/loback-console.xml

<included>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>${CONSOLE_LOG_PATTERN}</pattern>
      <charset>UTF-8</charset>
    </encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="CONSOLE" />
  </root>

</included>
logback/logback-file.xml

<included>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  <property name="LOG_FILE_BASE" value="myapp" />

  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>log/${LOG_FILE_BASE}.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <fileNamePattern>log/${LOG_FILE_BASE}.log.%d{yyyyMMdd}.%i</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
        <maxFileSize>500MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      <maxHistory>60</maxHistory>
    </rollingPolicy>
    <encoder>
      <pattern>${FILE_LOG_PATTERN}</pattern>
      <immediateFlush>${IMMEDIATE_FLUSH}</immediateFlush>
    </encoder>
  </appender>

  <appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
    <queueSize>1024</queueSize>
    <appender-ref ref="FILE" />
  </appender>

  <root level="INFO">
    <appender-ref ref="ASYNC_FILE" />
  </root>

</included>

이렇게 설정한 이후에는 Profile을 추가해서 실행해야 한다. localconsole-logging을 함께 Profile로 설정해서 실행하면 콘솔 로깅이 잘 출력되는 것을 확인할 수 있다.



마지막으로 localfile-logging을 Profile로 설정하고 실행해보면 마찬가지로 /logs 디렉토리 하위에 myapp.log 파일이 생성되고 이 안에 로깅이 쓰여진 것을 확인할 수 있다.

마무리

Project의 기본 설정과 Profile, Logback 설정을 마쳤다. 지금까지 만든 Application은 Spring Boot를 기반으로 Java Application으로서 동작한다. 다음에는 실제 Web Application으로 동작하기 위해서 Spring MVC를 적용해보고 Controller를 생성하여 간단하게 API 구현을 해본다.