본문으로 건너뛰기

SpringBoot 에서 Logging을 사용해보자

What is a Log?

  • 로그는 간단하게 말해서 연속된 데이터의 기록이라고 할 수 있다
  • 일반적으로 처음 프로그래밍을 배울 때는 보통 System.out.print 사용을 많이한다
  • 이 때, 프로그램이 실행되면서 콘솔에 무엇인가가 출력되는데, 이런 것들이 로그가 될 수 있다
  • Simply put, a log is a record of continuous data
  • When first learning programming, System.out.print is commonly used
  • When a program runs and outputs something to the console, these outputs can be considered logs

Logging Framework

  • System.out.print를 사용하여 디버깅을 할 수 있지만, 만약 어플리케이션의 사이즈가 커지게 되면 이런 방식은 너무 비효율적이다
  • 개발 후에 로그를 편하게 볼 수 있는 방법을 고려 해 놓는것도 좋은 프로그램을 개발하고 유지하기위한 방법이다
  • While you can debug using System.out.print, this approach becomes very inefficient as the application grows in size
  • Considering how to easily view logs after development is also a way to develop and maintain good programs

Slf4j

  • 로그를 남기고 추적하는 요구사항이 많이 생겨, 이와같은 요구사항들을 해소하고자 Loggin Framework이 생겼다
  • 스프링의 Logging Framework 중 가장 유명한 라이브러리는 Slf4j 이다
  • Slf4j를 사용하면 logback, log4j, log4j2 와 같은 구현체를 쉽게 교체하고 사용할 수 있다
  • As requirements for logging and tracking increased, Logging Frameworks were created to address these needs
  • The most famous library among Spring's Logging Frameworks is Slf4j
  • Using Slf4j, you can easily swap and use implementations such as logback, log4j, and log4j2

로깅을 적용 해 보자

Let's Apply Logging

  • 개발환경은 다음과 같다
    • SpringBoot
    • Kotlin
    • Gradle
  • The development environment is as follows
    • SpringBoot
    • Kotlin
    • Gradle

의존성 설정 (build.gradle.kts)

Dependency Configuration (build.gradle.kts)

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id("org.springframework.boot") version "2.7.1"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}

repositories {
mavenCentral()
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.springframework.boot:spring-boot-starter-log4j2")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

configurations.forEach {
it.exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging")
it.exclude(group = "org.apache.logging.log4j", module = "log4j-to-slf4j")
}


tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}

tasks.withType<Test> {
useJUnitPlatform()
}

log4j2 설정

log4j2 Configuration

  • xml 설정방법과 Jackson을 사용한 설정방법을 사용하여 log4j2 설정을 할 수 있다
  • Springboot의 resource 폴더 하위에 xml파일을 생성하자
  • 생성한 xml파일을 log4j2.xml로 저장하고, 아래의 내용을 적어준다
  • You can configure log4j2 using XML configuration or Jackson-based configuration
  • Create an xml file under the resource folder in Springboot
  • Save the created xml file as log4j2.xml and add the following content
  <?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="INFO">
<Properties>
<Property name="LOG_PATTERN">%d{HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
<Console name="ConsoleLog" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="ConsoleLog"/>
<AppenderRef ref="FileLog"/>
</Root>
</Loggers>
</Configuration>
  • Configuration: 로그 설정을 위한 최상위 요소
    • status 속성: Log4j2 내부의 동작에 대한 로깅 레벨을 설정 (log4j 내부 문제를 해결하기 위한 용도의 로깅이 필요한 경우 사용)
  • Properties: 하단 설정에 사용할 변수들을 정의
    • name: 위 예제에서 name="LOG_PATTERN"으로 설정하여 LOG_PATTERN이라는 변수를 정의
    • Appenders: 로그가 출력되는 위치
    • Console: 콘솔에 출력될 로그 설정
      • name: 어펜더의 이름
      • target: 로그 타겟 (default: SYSTEM_OUT)
    • PatternLayout: 로그의 패턴을 설정
  • Loggers: 로깅 작업의 주체로 각 패키지 별로 다양한 설정을 할 수 있음
    • Root: 모든 패키지에 대한 로깅을 하기 위한 일반적인 로그 정책 설정 (한 개만 설정할 수 있음)
      • AppenderRef: 상단에 설정한 Appender를 참조
  • Configuration: The top-level element for log configuration
    • status attribute: Sets the logging level for Log4j2 internal operations (used when internal logging is needed to troubleshoot log4j issues)
  • Properties: Defines variables to be used in the configuration below
    • name: In the example above, name="LOG_PATTERN" defines a variable called LOG_PATTERN
    • Appenders: Where logs are output
    • Console: Configuration for logs output to the console
      • name: Name of the appender
      • target: Log target (default: SYSTEM_OUT)
    • PatternLayout: Sets the log pattern
  • Loggers: The main entity for logging operations, allowing various settings for each package
    • Root: General log policy settings for logging all packages (only one can be set)
      • AppenderRef: References the Appender configured above

TestController 작성

Writing TestController

  • TestController를 작성하고, 해당 컨트롤러에서 Log를 출력하는 작업을 하자
  • Java에서는 Slf4j를 import하면, log를 바로 사용할 수 있지만 코틀린에서는 아래와같이 선언을 해 주어야한다.
  • Kotlin에서 Logger를 설정하는방법은 여러가지가 있기 때문에 참고하면 좋은 링크를 아래에 넣는다
  • Let's write a TestController and output logs from that controller
  • In Java, you can use log directly after importing Slf4j, but in Kotlin, you need to declare it as shown below
  • Since there are several ways to configure Logger in Kotlin, here is a useful reference link
package com.example.springkotlinloggingsample

import lombok.extern.slf4j.Slf4j
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping

@Slf4j
@Controller
class TestController {
val logger: Logger = LoggerFactory.getLogger(TestController::class.java)

@GetMapping("/")
fun index(): String {
logger.info("Hello, This is INFO Message")
logger.debug("Hello, This is DEBUG Message")
logger.trace("Hello, This is TRACE Message")
logger.warn("Hello, This is WARN Message")
logger.error("Hello, This is ERROR Message")
return "index"
}
}
  • 이제 api 호출을 통해, log를 출력 하면 아래와같이 로그가 출력되는것을 볼 수 있다
  • Now, when you output logs through an API call, you can see the logs output as follows
 [http-nio-8080-exec-2] INFO  com.example.springkotlinloggingsample.TestController - Hello, This is INFO Message
[http-nio-8080-exec-2] WARN com.example.springkotlinloggingsample.TestController - Hello, This is WARN Message
[http-nio-8080-exec-2] ERROR com.example.springkotlinloggingsample.TestController - Hello, This is ERROR Message
  • TRACE와 DEBUG는 출력되지 않는다
  • Logging의 레벨이 있는데, TRACE와 DEBUG는 로그레벨을 낮추는 설정을 추가로 해야 볼 수 있다
  • 로그의 레벨은 다음과 같다
  • TRACE and DEBUG are not output
  • There are logging levels, and TRACE and DEBUG require additional configuration to lower the log level to be visible
  • The log levels are as follows
TRACE > DEBUG > INFO > WARN > ERROR

로그를 파일로 남기자 (log4j2.xml을 수정하자)

Let's Save Logs to a File (Modify log4j2.xml)

  • 로그가 log4j2.xml에서 설정한 형태로 잘 출력이 되는 것을 볼 수 있다
  • 로그는 서버가 끊기더라도 다시 찾아 볼 수 있어야하기 떄문에, 기타 다른 플랫폼으로도 보내어 로그를 확인하는 방법들을 많이 사용한다
  • 이번 예제에서는 간단하기 "파일"로만 남기는 작업을 해 보려고 한다
  • log4j2.xml을 아래와같이 수정한다
  • You can see that logs are output correctly in the format configured in log4j2.xml
  • Since logs need to be retrievable even after the server disconnects, many methods are used to send logs to other platforms for viewing
  • In this example, we will simply save logs to a "file"
  • Modify log4j2.xml as follows
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="INFO">
<Properties>
<Property name="LOG_PATTERN">%d{HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n</Property>
</Properties>
<Appenders>
<Console name="ConsoleLog" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8"/>
</Console>
<RollingFile name="FileLog"
fileName="./logs/spring.log"
filePattern="./logs/spring-%d{yyyy-MM-dd}-%i.log">
<PatternLayout pattern="${LOG_PATTERN}" charset="UTF-8" />
<Policies>
<TimeBasedTriggeringPolicy interval="1" />
<SizeBasedTriggeringPolicy size="10000KB" />
</Policies>
<DefaultRolloverStrategy max="20" fileIndex="min" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="ConsoleLog" />
<AppenderRef ref="FileLog" />
</Root>
</Loggers>
</Configuration>
  • RollingFile: 조건에 따라 파일에 로그를 출력하도록 설정

    • name: 어펜더의 이름
    • fileName: 경로를 포함한 파일 이름
    • filePattern: 롤링 조건에 따른 경로를 포함한 파일 이름 패턴
    • Policies: 파일 롤링 정책
      • TimeBasedTriggeringPolicy: 1일 단위(interval=1)로 새로운 파일에 로그를 기록
      • SizeBasedTriggeringPolicy: 파일 사이즈를 기준으로 용량이 넘칠 경우 다음 파일을 생성하여 기록
      • DefaultRolloverStrategy: 파일 용량 초과 시 생성될 수 있는 파일의 최대 개수 설정
  • 위와같이 설정하고나면, log파일이 springboot 아래의 logs 폴더가 생성되며 쌓이게 된다

  • RollingFile: Configures log output to files based on conditions

    • name: Name of the appender
    • fileName: File name including path
    • filePattern: File name pattern including path according to rolling conditions
    • Policies: File rolling policies
      • TimeBasedTriggeringPolicy: Records logs to a new file on a daily basis (interval=1)
      • SizeBasedTriggeringPolicy: Creates a new file when the file size exceeds the limit
      • DefaultRolloverStrategy: Sets the maximum number of files that can be created when file capacity is exceeded
  • After configuring as above, a logs folder will be created under springboot and log files will accumulate there