Spring Boot 3.4를 사용하여 사용자 정의 헤더에서 추적을 전파하는 방법

Spring Boot 3.4를 사용하여 사용자 정의 헤더에서 추적을 전파하는 방법
Spring Boot 3.4를 사용하여 사용자 정의 헤더에서 추적을 전파하는 방법

Spring Boot 3.4에서 사용자 정의 헤더 추적 처리

두 클라이언트와 원활하게 작동하는 Spring Boot 3.4 웹 서비스가 있다고 상상해 보십시오. 첫 번째 클라이언트는 Spring Boot 3+를 사용하여 추적 전파를 쉽게 만듭니다. 추가 노력 없이도 아름다운 엔드 투 엔드 추적 연속성 🪄을 얻을 수 있습니다. 마치 마술처럼 로그가 깨끗하고 연결된 것처럼 보입니다.

그러나 클라이언트 2가 등장하면 상황이 반전됩니다. 표준 추적 헤더 대신 `ot-custom-traceid` 및 `ot-custom-spanid`와 같은 사용자 정의 헤더를 보냅니다. 이러한 사용자 정의 헤더에는 유효한 추적 정보가 포함되어 있지만 Spring Boot는 이러한 추적을 전파하지 못합니다. 결과는? 클라이언트 추적을 서버 측 로그와 연결하는 기능이 상실됩니다.

이로 인해 관측 가능성의 격차가 발생합니다. 클라이언트 1의 경우 서비스 전반에 걸친 요청의 전체 경로가 표시됩니다. 클라이언트 2의 경우 서버측 로그만 표시되며 중요한 클라이언트 추적이 누락됩니다. 그것은 마치 퍼즐의 절반을 보는 것과 같습니다. 뭔가 빠졌다는 것을 알지만 그 조각들을 하나로 합칠 수는 없습니다. 😓

이 기사에서는 Spring Cloud Sleuth에 의존하지 않고 Spring Boot 3.4 생태계를 그대로 유지하면서 이 문제를 해결하는 방법을 살펴보겠습니다. 결국에는 사용자 정의 헤더에서 추적을 전파하고 계속하여 시스템 전체에서 원활한 관찰 가능성을 보장하는 방법을 알게 될 것입니다.

명령 사용예
MDC.put 이 명령은 매핑된 진단 컨텍스트(MDC)에 키-값 쌍을 추가하여 사용자 지정 추적 ID가 로그에 포함될 수 있도록 합니다. 예를 들어 MDC.put("traceId", "12345")입니다.
MDC.clear 요청 간 추적 오염을 방지하기 위해 요청이 처리된 후 MDC에서 모든 항목을 지웁니다. 예를 들어 MDC.clear()입니다.
OncePerRequestFilter 필터 로직이 HTTP 요청당 한 번만 실행되도록 보장하는 Spring Boot 필터로, 헤더 추적에 이상적입니다. 예: 공용 클래스 CustomTraceFilter는 OncePerRequestFilter를 확장합니다.
filterChain.doFilter 체인의 다음 필터로 진행하여 요청이 다른 필터를 통해 계속되도록 합니다. 예를 들어 filterChain.doFilter(요청, 응답)입니다.
RestTemplate.getInterceptors() RestTemplate 인스턴스에 대한 인터셉터 목록을 검색하여 사용자 정의 인터셉터를 추가할 수 있습니다. 예: RestTemplate.getInterceptors().add(new CustomInterceptor()).
ClientHttpRequestInterceptor 나가는 HTTP 요청을 가로채고 사용자 정의 헤더를 추가하기 위한 인터페이스입니다. 예를 들어 추적 ID를 삽입하기 위해 ClientHttpRequestInterceptor를 구현합니다.
HttpServletRequest.getHeader 들어오는 요청에서 특정 HTTP 헤더의 값을 추출합니다. 예: request.getHeader("ot-custom-traceid").
FilterRegistrationBean Spring Boot 애플리케이션에 사용자 정의 필터를 등록합니다. 예: RegistrationBean.setFilter(new CustomTraceFilter()).
MockMvc.perform Spring Boot 애플리케이션에 대한 단위 테스트에서 HTTP 요청을 시뮬레이션합니다. 예: mockMvc.perform(get("/test-endpoint").header("ot-custom-traceid", "12345")).
ClientHttpRequestExecution.execute 제공된 요청 본문 및 헤더를 사용하여 가로채는 HTTP 요청을 실행합니다. 예: Execution.execute(요청, 본문).

Spring Boot의 사용자 정의 헤더 추적 전파

이 문제를 해결하는 핵심 구성 요소 중 하나는 CustomTraceFilter입니다. 이 필터는 OncePerRequest필터 클래스를 사용하여 각 HTTP 요청에 대해 추적 헤더 논리가 한 번만 실행되도록 합니다. Spring Boot의 필터는 요청이나 응답을 전역적으로 수정할 때 매우 유용합니다. 예를 들어 클라이언트가 다음과 같은 추적 정보를 보내는 경우 ot-맞춤-추적 또는 ot-맞춤형-spanid 사용자 정의 헤더에서 이 필터는 요청을 가로채서 이러한 헤더를 추출하고 이를 매핑된 진단 컨텍스트(MDC)로 전파합니다. MDC에 추적 ID를 추가하면 요청 처리 중에 생성된 로그에 이러한 식별자가 표시됩니다.

MDC는 SLF4J 및 Logback과 같은 로깅 프레임워크의 중요한 부분입니다. 이를 통해 사용자 정의 추적 ID와 같은 현재 스레드에 대한 상황별 정보를 저장할 수 있습니다. 다음과 같은 명령을 사용하여 MDC.put 그리고 MDC.clear, 로깅 시스템에 추적 세부 정보가 포함되고 동시 요청 간의 오염이 방지되는지 확인합니다. 예를 들어 클라이언트 2가 `ot-custom-traceid`를 `8f7ebd8a73f9a8f50e6a00a87a20952a`로 보내는 경우 이 ID는 MDC에 저장되고 모든 다운스트림 로그에 포함되어 일관된 추적 경로를 생성합니다.

반면에 나가는 HTTP 요청의 경우 RestTemplate 인터셉터는 필수적인 역할을 합니다. 구현함으로써 클라이언트Http요청인터셉터, 동일한 추적 헤더(`ot-custom-traceid` 및 `ot-custom-spanid`)를 나가는 요청에 첨부할 수 있습니다. 이렇게 하면 애플리케이션이 다른 마이크로서비스를 호출할 때 추적 연속성이 유지됩니다. 예를 들어 서버가 추적 ID '8f7ebd8a73f9a8f50e6a00a87a20952a'를 사용하여 요청을 처리할 때 이 ID를 발신 헤더에 첨부하므로 다운스트림 서비스가 추적을 원활하게 인식하고 전파할 수 있습니다.

마지막으로 MockMvc로 작성된 단위 테스트는 HTTP 요청을 시뮬레이션하고 헤더 전파를 확인하여 전체 설정을 검증합니다. 실제 애플리케이션에서는 추적 헤더가 올바르게 처리되는지 확인하는 테스트가 중요합니다. 예를 들어 사용자 정의 헤더가 포함된 GET 요청을 보내고 응답이나 로그를 검사하면 필터와 인터셉터가 예상대로 작동하는지 확인할 수 있습니다. 이 포괄적인 접근 방식은 Spring Cloud Sleuth와 같은 레거시 종속성에 의존하지 않고 문제를 해결합니다. 궁극적으로 필터, 인터셉터 및 MDC의 조합은 클라이언트가 사용자 정의 헤더를 사용하는 경우에도 추적 연속성을 보장하여 시스템을 강력하고 완전히 관찰 가능하게 만듭니다. 🌟

Spring Boot 3.4에서 사용자 정의 추적 헤더 전파

백엔드 처리를 위해 Spring Boot 3.4 및 Micrometer와 함께 Java 사용

// Solution 1: Extract and Propagate Custom Trace Headers Manually
// Import necessary Spring Boot and Micrometer libraries
import org.slf4j.MDC;
import org.springframework.http.HttpHeaders;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomTraceFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws IOException {
        String traceId = request.getHeader("ot-custom-traceid");
        String spanId = request.getHeader("ot-custom-spanid");
        try {
            if (traceId != null) {
                MDC.put("traceId", traceId); // Add traceId to Mapped Diagnostic Context
            }
            if (spanId != null) {
                MDC.put("spanId", spanId);
            }
            filterChain.doFilter(request, response); // Continue request processing
        } finally {
            MDC.clear(); // Ensure MDC is cleared after processing
        }
    }
}

// Register the filter in your configuration class
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<CustomTraceFilter> traceFilter() {
        FilterRegistrationBean<CustomTraceFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new CustomTraceFilter());
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }
}

사용자 정의 추적 헤더 전파를 위한 단위 테스트

추적 헤더 전파를 검증하기 위해 JUnit 및 MockMvc를 사용한 테스트

// Import necessary libraries
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
public class CustomTraceFilterTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testCustomTraceHeaders() throws Exception {
        mockMvc.perform(get("/test-endpoint")
                .header("ot-custom-traceid", "12345")
                .header("ot-custom-spanid", "67890"))
                .andExpect(status().isOk());
    }
}

RestTemplate을 사용하여 HTTP 요청에 사용자 정의 헤더 전파

RestTemplate 인터셉터를 사용하여 나가는 요청에 사용자 정의 헤더 추가

// Import necessary libraries
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;

public class CustomHeaderInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        HttpHeaders headers = request.getHeaders();
        headers.add("ot-custom-traceid", "12345");
        headers.add("ot-custom-spanid", "67890");
        return execution.execute(request, body);
    }
}

// Register the interceptor with RestTemplate
@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getInterceptors().add(new CustomHeaderInterceptor());
        return restTemplate;
    }
}

Spring Boot 3.4에서 OpenTelemetry를 사용하여 사용자 정의 헤더 추적 처리

Spring Boot 3.4로 작업할 때 사용자 정의 헤더에서 추적을 전파하는 또 다른 강력한 접근 방식은 다음을 통합하는 것입니다. 오픈텔레메트리. 오픈 소스 관찰 프레임워크인 OpenTelemetry는 추적을 원활하게 계측, 수집 및 내보내는 데 도움이 됩니다. 다음과 같은 사용자 정의 헤더를 포함하여 추적 컨텍스트를 추출하고 주입하는 메커니즘을 제공합니다. ot-맞춤-추적 그리고 ot-맞춤형-spanid, 귀하의 응용 프로그램에. OpenTelemetry의 TextMapPropagator를 활용하면 비표준 클라이언트와 관찰 시스템 간의 격차를 해소할 수 있습니다.

Spring Boot 3.4에서 OpenTelemetry를 사용하려면 사용자 정의 전파자를 구현하여 사용자 정의 헤더에서 추적 정보를 추출하고 이를 현재 추적 컨텍스트에 연결할 수 있습니다. 예를 들어 서버가 클라이언트 2로부터 들어오는 요청을 받으면 OpenTelemetry는 사용자 지정 헤더를 구문 분석하고 원래 추적 컨텍스트를 재구성할 수 있습니다. 이렇게 하면 다운스트림 서비스에서 동일한 추적 ID를 확인하여 엔드투엔드 가시성을 확보할 수 있습니다. Spring Cloud Sleuth와 같은 이전 솔루션과 달리 OpenTelemetry는 가벼우며 최신 관측 가능성 표준을 준수합니다.

OpenTelemetry의 전파자를 마이크로미터와 결합하면 추적 정보를 통해 측정항목과 로깅을 강화할 수 있습니다. 관찰 도구에서 클라이언트 1과 클라이언트 2 모두에서 들어오는 요청에 대한 추적을 원활하게 보는 것을 상상해 보십시오. OpenTelemetry는 Prometheus, Zipkin 또는 Jaeger와의 통합을 자동으로 지원하므로 추적 시각화를 중앙 집중화할 수 있습니다. 이 접근 방식을 사용하면 사용자 정의 헤더가 포함된 경우에도 추적 데이터가 손실되지 않으며 디버깅이 훨씬 쉬워집니다. 🚀

Spring Boot에서 사용자 정의 추적 전파에 대해 자주 묻는 질문

  1. Spring Boot에서 사용자 정의 추적 헤더를 수동으로 추출하려면 어떻게 해야 합니까?
  2. request.getHeader("custom-header")를 사용하여 특정 헤더를 수동으로 가져오고 MDC.put("traceId", value)를 사용하여 MDC에 추가할 수 있습니다.
  3. 사용자 지정 추적 전파에 OpenTelemetry를 사용하면 어떤 이점이 있나요?
  4. OpenTelemetry는 마이크로서비스 전반에 걸쳐 사용자 지정 헤더를 포함한 추적을 전파하는 공급업체 중립적인 최신 접근 방식을 제공합니다.
  5. Spring Boot에서 RestTemplate을 사용하여 사용자 정의 헤더를 전파할 수 있나요?
  6. 예, ClientHttpRequestInterceptor를 구현하면 traceid 및 spanid와 같은 사용자 정의 헤더를 나가는 요청에 연결할 수 있습니다.
  7. 헤더를 전체적으로 캡처하려면 필터를 어떻게 등록합니까?
  8. OncePerRequestFilter를 확장하는 필터를 생성하고 FilterRegistrationBean을 사용하여 이를 등록하여 모든 엔드포인트에 대한 헤더를 캡처할 수 있습니다.
  9. Spring Boot의 추적을 시각화하는 데 어떤 도구를 사용할 수 있나요?
  10. Zipkin, Jaeger, Prometheus와 같은 도구는 Spring Boot 및 OpenTelemetry와 통합되어 엔드투엔드 추적을 시각화할 수 있습니다.

원활한 추적 연속성 보장

최신 시스템에서는 안정적인 관찰을 위해 사용자 정의 추적 헤더를 처리하는 것이 중요합니다. 필터와 인터셉터를 사용하면 클라이언트가 제공한 추적 정보를 캡처하고 이를 서비스 전체에 올바르게 전파할 수 있습니다. 이렇게 하면 조각난 로그와 추적 누락을 방지할 수 있습니다. 🔍

Micrometer 또는 OpenTelemetry와 결합된 Spring Boot 3.4를 사용하면 Spring Cloud Sleuth와 같은 이전 도구에 의존하지 않고도 강력한 솔루션을 사용할 수 있습니다. 클라이언트 1의 표준 헤더를 처리하든 클라이언트 2의 사용자 정의 헤더를 처리하든 이러한 기술을 구현하면 추적 격차를 효율적으로 메울 수 있습니다. 🚀

출처 및 참고자료
  1. Spring Boot 공식 문서: 추적 컨텍스트 전파. 스프링 부트 문서
  2. Java 개발자를 위한 OpenTelemetry: 추적 전파 가이드. OpenTelemetry 자바
  3. 마이크로미터 관측 가능성 문서: 맞춤형 추적 헤더 통합. 마이크로미터 관찰성
  4. SLF4J 로깅 API: 매핑된 진단 컨텍스트(MDC) 사용 사례. SLF4J 매뉴얼