Spring Boot - Log4j2, MDC

gzip·2023년 6월 1일
2

Spring

목록 보기
1/3

Spring Boot에 Log4j2와 MDC 적용해서 쓰레드별 식별 가능한 로그 남기기

Spring Boot: 3.1.0


Log4j2

스프링 부트에서는 Logback이 기본 로거로 설정되어 있다.

spring-boot-starter 모듈은 spring-boot-starter-logging 모듈을 포함하고 있고 spring-boot-starter-logging에는 SLF4J와 구현체인 Logback를 포함하고 있다.

Logback보다 퍼포먼스가 더 좋은 Log4j2를 적용해보자.


Log4j2 설정

build.gradle.kts

dependencies {  
	// ...
  
	// Log4j2  
	implementation("org.springframework.boot:spring-boot-starter-log4j2")  
	modules {  
		module("org.springframework.boot:spring-boot-starter-logging") {  
			replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback")  
		}  
	}
}
  • spring-boot-starter-logging 모듈을 spring-boot-starter-log4j2로 교체

application.yml

env:  
	root-path: .
  • Log4j2 설정에서 스프링 설정 파일의 값을 사용할 수 있어서 굳이 추가해 봤다.

src/main/resources/log4j2-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Properties>
        <!-- 로그 파일 디렉토리 경로 -->
        <Property name="LOG_FILE_PATH">${spring:env.root-path}/logs</Property>
        <!-- 로그 파일 이름 -->
        <property name="LOG_FILE_NAME" value="${LOG_FILE_PATH}/app.log"/>
        <!-- 로그 파일 패턴 -->
        <property name="LOG_FILE_PATTERN" value="${LOG_FILE_PATH}/app_%d{yyyy-MM-dd}_%i.log.gz"/>
        <!-- 로그 패턴 -->
        <property name="LOG_PATTERN"
                  value="[%d{yyyy.MM.dd HH:mm:ss.SSS}] %5level - [%10.10t] %-40.40logger{39} : %msg %n"/>
    </Properties>

    <Appenders>
        <Console name="STDOUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>

        <RollingFile
                name="RollingFile"
                fileName="${LOG_FILE_NAME}"
                filePattern="${LOG_FILE_PATTERN}">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <Policies>
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="256 MB"/>
            </Policies>
        </RollingFile>
    </Appenders>

    <Loggers>
        <Root level="info">
            <SpringProfile name="!prod">
                <AppenderRef ref="STDOUT"/>
            </SpringProfile>
            <AppenderRef ref="RollingFile"/>
        </Root>
    </Loggers>
</Configuration>
  • ${spring:env.root-path} 처럼 사용하면 스프링 설정 파일에서 properties를 읽어 올 수 있다.

결과


참고


MDC (Mapped Diagnostic Context)

멀티 쓰레드 환경에서 MDC를 적용하여 쓰레드별로 식별 가능한 로그 남기기

서블릿에 필터를 추가하여 MDC를 적용해보자.


MDC 설정

MDCFilter.java

package com.example.demo;  
  
import jakarta.servlet.*;  
import org.slf4j.MDC;  
import org.springframework.core.Ordered;  
import org.springframework.core.annotation.Order;  
import org.springframework.stereotype.Component;  
  
import java.io.IOException;  
import java.util.UUID;  
  
@Component  
@Order(Ordered.HIGHEST_PRECEDENCE) // 최고 우선 순위  
public class MDCFilter implements Filter {  
  
	private static final String MDC_KEY = "trx_id";  
	  
	@Override  
	public void doFilter(  
			ServletRequest request,  
			ServletResponse response,  
			FilterChain chain  
	) throws IOException, ServletException {  
	  
		final UUID uuid = UUID.randomUUID();  
		MDC.put(MDC_KEY, uuid.toString());  
		  
		chain.doFilter(request, response);  
		  
		// MDC.remove(MDC_KEY);  
		MDC.clear();  
	}  
}
  • slf4j.MDC은 쓰레드 로컬을 사용하기 때문에 요청 종료 전에 쓰레드 로컬을 비워줘야 한다.
    - 쓰레드 풀을 사용할 경우 쓰레드는 재사용 된다.
    - 때문에 다른 요청에서 같은 쓰레드를 사용하게 되면 값을 공유하게 되고 문제가 생길 수 있다.

log4j2-spring.xml 수정

<Configuration>
    <Properties>
        <!-- ... -->
        
        <!-- 로그 패턴 -->
        <property name="LOG_PATTERN"
                  value="[%d{yyyy.MM.dd HH:mm:ss.SSS}] %5level - [%10.10t] [%.8equals{%X{trx_id}}{}{ TRX_ID }] %-40.40logger{39} : %msg %n"/>
    
		<!-- ... -->
</Configuration>
  • LOG_PATTERN의 value를 위와 같이 변경
  • UUID의 8자리만 남기게 하였다.

HelloController.java

package com.example.demo;  
  
import lombok.extern.log4j.Log4j2;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  
  
@Log4j2  
@RestController  
@RequestMapping("hello")  
public class HelloController {  
  
	@GetMapping()  
	public void hello() throws InterruptedException {  
		log.info("hello - start");  
		Thread.sleep(1000);  
		log.info("hello - end");  
	}  
}
  • 로그 확인용 컨트롤러

결과

0개의 댓글