MDC

Yellta·2024년 12월 23일

Spring

목록 보기
1/2

MDC(Mapped Diagnostic Context)란?

LogBack프레임워크의 기능 중 하나이며, 로그 메세지에 컨텍스트 정보를 저장할 수 있도록 한다.
MDC는 로깅 이벤트 간에 전파되는 키-값 저장소이며, userId나 requestId같은 정보를 저장하는 데 사용할 수 있다.

추가
  • LogBack은 Spring의 기본 로깅 시스템이다.
  • 컨텍스트 정보란 특정 로그 메세지가 발생한 상황에 대한 추가적인 정보를 의미한다. (요청ID, 사용자 정보, 세션 정보 등등)

MDC코드 예시

public class LogBackProgrammingConfig {  
    public static void main(String[] args) {  
       LoggerContext context = (LoggerContext)LoggerFactory.getILoggerFactory();  
       // Console Appender 생성  
       ConsoleAppender consoleAppender = new ConsoleAppender();  
       consoleAppender.setContext(context);  
  
       // 패턴 레이아웃 설정  
       PatternLayoutEncoder encoder = new PatternLayoutEncoder();  
       encoder.setContext(context);  
       //날짜 + 시간 스레드 이름 출력 / 로그를 호출한 클래스 이름/ MDC에서 지정한 키 value값 
       encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - [requestId=%X{requestId}] %msg%n");  
       encoder.start();  
  
       // Appender와 Encoder 연결  
       consoleAppender.setEncoder(encoder);  
       consoleAppender.start();  
  
       // Root Logger 설정  
       ch.qos.logback.classic.Logger rootLogger = context.getLogger("ROOT");  
       rootLogger.addAppender(consoleAppender);  
  
       // 테스트 로그  
       Logger logger = LoggerFactory.getLogger(LogBackProgrammingConfig.class);  
       logger.info("This is a test log with MDC.");  
    }  
}

결과

00:22:57.048 [main] INFO org.example.springstudy.MDC.LogBackProgrammingConfig -- This is a test log with MDC.
2024-12-20 00:22:57 [main] INFO  o.e.s.MDC.LogBackProgrammingConfig - [requestId=] This is a test log with MDC.

위의 결과는 main thread/ 클래스이름/ requestId/ 로그값
을 확인할 수 있다.

참고로 로그가 2개 뜬 이유는 Spring 기본 셋업과 겹쳐서 그렇다.

그렇다면 멀티 스레드 환경에서 로그는 어떻게 나올까?

public class MDCTest {  
  
    private static final Logger logger = LoggerFactory.getLogger(MDCTest.class);  
  
    public static void main(String[] args) {  
       // 스레드 풀 생성  
       ExecutorService executorService = Executors.newFixedThreadPool(3);  
  
       // 서로 다른 스레드에서 MDC 값을 설정하고 로그 출력  
       for (int i = 1; i <= 3; i++) {  
          int threadId = i;  
          executorService.submit(() -> {  
             MDC.put("requestId", "request-" + threadId); // 스레드별로 고유한 값 설정  
             MDC.put("userId", "user-" + threadId);       // 사용자 ID 설정  
  
             try {  
                logger.info("Processing request in thread {}", threadId);  
             } finally {  
                MDC.clear(); // MDC 데이터 정리  
             }  
          });  
       }  
  
       executorService.shutdown();  
    }  
  
}

멀티 스레드 환경을 만들고 MDC값을 설정하고 로그를 출력했다.

2024-12-20 00:32:14 [pool-1-thread-1] INFO  org.example.springstudy.MDC.MDCTest - [requestId=request-1,
                userId=user-1] Processing request in thread 1
2024-12-20 00:32:14 [pool-1-thread-2] INFO  org.example.springstudy.MDC.MDCTest - [requestId=request-2,
                userId=user-2] Processing request in thread 2
2024-12-20 00:32:14 [pool-1-thread-3] INFO  org.example.springstudy.MDC.MDCTest - [requestId=request-3,
                userId=user-3] Processing request in thread 3

이제 스프링 로그가 위와 같은 모습으로 뜨는 것을 확인할 수 있다.

참고로 log template을 지정하기 위해 xml파일을 resources폴더에 따로 지정하고, application.properties에 로그 설정수행했다.

logging.config=classpath:logback.xml
<configuration>  
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">  
        <encoder>  
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - [requestId=%X{requestId},  
                userId=%X{userId}] %msg%n  
            </pattern>  
        </encoder>  
    </appender>  
  
    <root level="info">  
        <appender-ref ref="CONSOLE"/>  
    </root>  
</configuration>

그럼 MDC를 사용하는 이유는 뭘까?

  • 로그에 requestID, userId, sessionID와 같은 정보를 표시할 수 있다. (어떤 스레드에서 사용하는지도 알 수 있다.)
  • 분산 시스템 환경에서 요청 추적
    마이크로 서비스나, 분산 서비스 아키텍처에서 들어오는 요청을 추적할 수 있다. 예를 들어 서로다른 서비스에 요청이 들어오고 traceID를 MDC에 지정하면 어떤 시스템으로 요청이 들어오는지 확인할 수 있다.

그럼 MDC의 단점은 뭘까?

  • MDC는 thread-local이다. 즉 데이터가 자동으로 child-thread나 스레드 풀의 다른 thread에 전달되지 않는다. 스레드가 스레드를 발생시키는 멀티스레드, 비동기에서는 좋지 않을 수 있다.
    새로운 작업이 쓰레드 풀에서 실행되면 MDC가 초기화된 상태로 실행되기 때문에 requestId, userId, 같은 컨텍스트 정보가 누락된다.
    또한 thread pool에서 스레드를 재사용할 경우, 이전 작업에서 설정한 MDC 데이터가 남아있어서 잘못된 로그가 기록될 수 있다.

참고

https://medium.com/javarevisited/mapped-diagnostic-context-mdc-6447b598736d

profile
저 이제 풀스택하려구요 flutter + express.js공부하렵니다.

0개의 댓글