스프링부트 3.x부터 Spring cloud sleuth로 트레이싱 기능을 추가할 수 없게 되었습니다.
마이그레이션 가이드
https://github.com/micrometer-metrics/tracing/wiki/Spring-Cloud-Sleuth-3.1-Migration-Guide
Spring Cloud Sleuth 3.1 Migration Guide
Provides tracing abstractions over tracers and tracing system reporters. - micrometer-metrics/tracing
github.com
앞으로는 Micrometer 의 brave 의존성을 추가해 트레이싱 기능을 사용할 수 있습니다.
적용해 볼 환경은 아래와 같습니다.
Spring boot 3.1.3
java 17
먼저 micrometer 의 brave 의존성을 추가해 줍니다.
zipkin 은 사용하지 않아 제외해 주었습니다. 사용하시는 분들은 exclude를 제거해 주세요.
implementation("io.micrometer:micrometer-tracing-bridge-brave") {
exclude group: "io.zipkin.reporter2"
}
traceId 및 spanId를 확인할 수 있도록 로그 패턴을 변경해 줍니다.
저는 logback xml 파일에서 설정하였습니다.
[%-5level] %d{yyyy-MM-dd HH:mm:ss} [%thread] %replace([%X{traceId}, %X{spanId}]){'\[, \]',''}[%logger{0}:%line] - %msg%n
%replace 부분을 쓰지 않으면 traceId, spanId 가 없을 때 빈 값이 보이게 됩니다.
ex) 2024-04-07T14:14:20.632+09:00 [INFO ] 2024-04-07 14:14:20 [restartedMain] [, ] [OptionalLiveReloadServer:59] - LiveReload server is running on port 35729
여러 가지 상황을 테스트해보기 위해 아래와 같은 컨트롤러 서비스를 만듭니다.
AsyncConfig.java
@EnableAsync
@Configuration
public class AsyncConfig {
}
@EnableAsync를 통해 @Async 기능을 활성화해줍니다.
TracingService.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class TracingService {
public void print() {
log.info("기존 스레드를 입니다.");
}
@Async
public void asyncPrint() {
log.info("@Async 에 의한 스레드 입니다.");
}
}
3가지 케이스에 대한 테스트를 진행하기 위해 메서드를 추가합니다.
1. 기본 호출 print()
2. @Async 어노테이션을 통한 비동기 호출 asyncPrint()
3. Executors를 통한 비동기 호출 executorsPrint()
TracingController.java
import com.infitry.laboratory.service.tracing.TracingService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("/tracing")
public class TracingController {
private final Executor executor;
private final TracingService tracingService;
@GetMapping("/test")
public void testTracing() {
tracingService.print();
executor.execute(() -> log.info("executors 에 의한 스레드입니다."));
tracingService.asyncPrint();
}
}
각 메서드를 호출하도록 작성합니다.
이제 테스트로 컨트롤러를 호출합니다.
[INFO ] 2024-04-07 14:36:26 [http-nio-8080-exec-1] [661230da8761fca64322fbb40c3b2848, 4322fbb40c3b2848][TracingService:14] - 기존 스레드를 입니다.
[INFO ] 2024-04-07 14:36:26 [pool-5-thread-1] [TracingService:23] - executors 에 의한 스레드입니다.
[INFO ] 2024-04-07 14:36:26 [task-1] [TracingService:18] - @Async 에 의한 스레드 입니다.
예상했던 결과는 3가지 케이스 모두 traceId, spanId 가 생기길 기대하였습니다.
그런데 기본 호출에 대해서만 생성되었습니다. [661230da8761fca64322fbb40c3b2848, 4322fbb40c3b2848]
문서를 확인해 보니 비동기 스레드에 대해 tracing 기능을 사용하려면 별도로 커스터마이징이 필요합니다.
Spring Cloud Sleuth 3.1 Migration Guide
Provides tracing abstractions over tracers and tracing system reporters. - micrometer-metrics/tracing
github.com
동일하게 적용하기 위해 기존 AsyncConfig 다음과 같이 수정합니다.
* 위 예시코드에서 ContextSnapshotFactory 인터페이스는 스프링부트 3.1.3 이상부터 존재하는 것 같네요.
import io.micrometer.context.ContextExecutorService;
import io.micrometer.context.ContextScheduledExecutorService;
import io.micrometer.context.ContextSnapshot;
import io.micrometer.context.ContextSnapshotFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.concurrent.*;
@EnableAsync
@Configuration(proxyBeanMethods = false)
public class AsyncConfig implements AsyncConfigurer, WebMvcConfigurer {
@Override
public Executor getAsyncExecutor() {
return ContextExecutorService.wrap(Executors.newCachedThreadPool(), ContextSnapshot::captureAll);
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(new SimpleAsyncTaskExecutor(r -> new Thread(ContextSnapshotFactory.builder().build().captureAll().wrap(r))));
}
@Bean
public ThreadPoolTaskScheduler taskExecutor() {
var threadPoolTaskScheduler = new ThreadPoolTaskScheduler() {
@Override
public ScheduledExecutorService getScheduledExecutor() throws IllegalStateException {
return ContextScheduledExecutorService.wrap(super.getScheduledExecutor());
}
};
threadPoolTaskScheduler.initialize();
return threadPoolTaskScheduler;
}
}
수정 후 다시 실행해 봅니다.
[INFO ] 2024-04-07 15:02:35 [http-nio-8080-exec-1] [661236fbbd55bf9f47bf8c35a9b5fb44, 47bf8c35a9b5fb44][TracingService:14] - 기존 스레드를 입니다.
[INFO ] 2024-04-07 15:02:35 [taskExecutor-1] [661236fbbd55bf9f47bf8c35a9b5fb44, 47bf8c35a9b5fb44][TracingController:24] - executors 에 의한 스레드입니다.
[INFO ] 2024-04-07 15:02:35 [pool-5-thread-1] [661236fbbd55bf9f47bf8c35a9b5fb44, 47bf8c35a9b5fb44][TracingService:18] - @Async 에 의한 스레드 입니다.
다음과 같이 스레드명은 모두 다르나 (http-nio-8080-exec-1, taskExecutor-1, poo-5-thread-1)
traceId, spanId(661236fbbd55bf9f47bf8c35a9b5fb44, 47bf8c35a9b5fb44) 는 모두 같아 비동기 호출이 존재하더라도 해당 요청을 추적 할 수 있게 되었습니다.
아직 sleuth 에서 micrometer 로 프로젝트가 이동한지 얼마되지 않아 불편한 점이 많은 것 같습니다. (async wrap 등..)
정확한 부분은 조금 더 여러가지 상황에서 테스트 해봐야 알 수 있을 것 같습니다.
'백엔드 > Spring Boot' 카테고리의 다른 글
Hikari CP 모니터링하기 (0) | 2024.06.06 |
---|---|
Spring, Spring Boot 버전 별 접미사 (0) | 2022.07.18 |
logback 취약점 (CVE-2021-42550) (0) | 2021.12.21 |
웹 페이지 성능 최적화 (0) | 2021.11.22 |