티스토리 뷰

개요

  • org.slf4j.MDC 클래스는 애플리케이션의 로그에 담을 정보를 저장할 수 있는 일종의 로그 정보 저장소로 유용하게 사용된다. MDC의 특징은 ThreadLocal에 의해 현재 쓰레드 범위 내에서만 사용할 수 있다는 것인데, 문제는 다른 쓰레드에서 실행되는 비동기 실행시 새로운 MDC 영역을 가지게 되어 호출한 쓰레드와의 연관성을 로그로 남기는 것이 불가능하다는 문제점이 있다. 해결책이 없는 것은 아니다. 비동기 실행시 제공될 쓰레드를 관리하는 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor에 쓰레드 실행시 MDC를 복제해주는 로직을 삽입하면 된다. 이번 글에서는 이 방법을 설명하고자 한다.

기존 문제점

  • 비동기 쓰레드와 호출한 쓰레드의 MDC 영역이 달라 서로 연관된 로그를 남기는 것이 불가능하다.

해결책으로 인한 기대효과

  • 비동기로 실행되는 쓰레드에서 자신을 호출한 쓰레드가 저장한 MDC 필드 정보를 온전히 전달 받을 수 있다. 각 비동기 쓰레드마다 로그를 남길 경우 자신을 호출한 쓰레드를 알 수 있어 디버그시 추적이 용이해진다.

TaskDecorator 구현체 작성

  • 가장 먼저 TaskDecorator 구현체를 아래와 같이 작성한다.
package com.jsonobject.example;

import java.util.Map;
import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;

public class LoggingTaskDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable task) {

        Map<String, String> callerThreadContext = MDC.getCopyOfContextMap();
        return () -> {
            MDC.setContextMap(callerThreadContext);
            task.run();
        };
    }
}
  • TaskDecorator를 작성하면 마치 Spring AOP을 사용하는 것과 같이 ThreadPoolTaskExecutor에 의해 관리되는 쓰레드가 매번 사용될 때마다 실행 전후에 끼어들어 동작을 제어할 수 있다. 위 예제는 자신을 호출한 쓰레드의 MDC 정보를 복제함으로서 동일한 MDC 정보를 가진채 쓰레드를 실행하게 한다.

TaskExecutor 빈 작성

  • TaskExecutor 빈을 아래와 같이 생성되도록 작성한다.
package com.jsonobject.example;

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.task.TaskExecutor
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

@Configuration
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {

        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setQueueCapacity(20);
        taskExecutor.setMaxPoolSize(30);
        taskExecutor.setTaskDecorator(new LoggingTaskDecorator());

        return taskExecutor;
    }
}
  • 위 예제는 setTaskDecorator() 메써드를 통해 앞서 작성한 LoggingTaskDecorator가 작동하도록 설정한 것이다.

참고할만한 다른 글

참고 글

댓글
댓글쓰기 폼