Spring Boot 3.4 を使用してカスタム ヘッダーからトレースを伝播する方法

Tracing

Spring Boot 3.4 でのカスタム ヘッダー トレースの処理

2 つのクライアントとシームレスに動作する Spring Boot 3.4 Web サービスがあると想像してください。最初のクライアントは 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 リクエストごとに 1 回だけ実行されるようにする Spring Boot フィルタ。ヘッダーのトレースに最適です。例: public クラス CustomTraceFilter は OncePerRequestFilter を拡張します。
filterChain.doFilter チェーン内の次のフィルターに進み、リクエストが他のフィルターを通過するようにします。たとえば、filterChain.doFilter(リクエスト、レスポンス)。
RestTemplate.getInterceptors() RestTemplate インスタンスのインターセプターのリストを取得し、カスタム インターセプターを追加できるようにします。例:restTemplate.getInterceptors().add(new CustomInterceptor())。
ClientHttpRequestInterceptor 送信 HTTP リクエストをインターセプトし、カスタム ヘッダーを追加するためのインターフェイス。たとえば、ClientHttpRequestInterceptor を実装してトレース ID を挿入します。
HttpServletRequest.getHeader 受信リクエストから特定の HTTP ヘッダーの値を抽出します。例: request.getHeader("ot-custom-traceid")。
FilterRegistrationBean Spring Boot アプリケーションにカスタム フィルターを登録します。例: registerBean.setFilter(new CustomTraceFilter())。
MockMvc.perform Spring Boot アプリケーションの単体テストで HTTP リクエストをシミュレートします。例:mockMvc.perform(get("/test-endpoint").header("ot-custom-traceid", "12345"))。
ClientHttpRequestExecution.execute インターセプトされた HTTP リクエストを、指定されたリクエスト本文とヘッダーを使用して実行します。例: 実行.実行(リクエスト, 本体)。

Spring Boot でのカスタムヘッダートレースの伝播

この問題を解決するための重要なコンポーネントの 1 つは CustomTraceFilter です。このフィルターは、 クラスを使用して、トレース ヘッダー ロジックが HTTP リクエストごとに 1 回だけ実行されるようにします。 Spring Boot のフィルターは、リクエストやレスポンスをグローバルに変更する場合に非常に役立ちます。たとえば、クライアントが次のようなトレース情報を送信した場合、 または カスタム ヘッダーでは、このフィルターはリクエストをインターセプトし、これらのヘッダーを抽出して、マップされた診断コンテキスト (MDC) に伝播します。トレース ID を MDC に追加することで、リクエストの処理中に生成されるログにこれらの識別子が表示されるようになります。

MDC は、SLF4J や Logback などのログ フレームワークの重要な部分です。これにより、カスタム トレース ID など、現在のスレッドのコンテキスト情報を保存できます。次のようなコマンドを使用する そして 、ログ システムにトレースの詳細が含まれていることを確認し、同時リクエスト間の汚染を回避します。たとえば、クライアント 2 が「ot-custom-traceid」を「8f7ebd8a73f9a8f50e6a00a87a20952a」として送信した場合、この ID は MDC に保存され、すべてのダウンストリーム ログに含まれ、一貫したトレース パスが作成されます。

一方、送信 HTTP リクエストの場合、RestTemplate インターセプター が重要な役割を果たします。実装することで 、同じトレース ヘッダー (`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 を使用する場合、カスタム ヘッダーからトレースを伝播するもう 1 つの強力なアプローチは、 。オープンソースの可観測性フレームワークである OpenTelemetry は、トレースのシームレスな計測、収集、エクスポートに役立ちます。これは、次のようなカスタム ヘッダーを含む、トレース コンテキストを抽出および挿入するメカニズムを提供します。 そして をアプリケーションに追加します。 OpenTelemetry の TextMapPropagator を活用することで、非標準クライアントと可観測性システムの間のギャップを埋めることができます。

Spring Boot 3.4 で OpenTelemetry を使用するには、カスタム プロパゲーター を実装して、カスタム ヘッダーからトレース情報を抽出し、それを現在のトレース コンテキストに添付できます。たとえば、サーバーがクライアント 2 から受信リクエストを受信すると、OpenTelemetry はカスタム ヘッダーを解析し、元のトレース コンテキストを再構築できます。これにより、ダウンストリーム サービスが同じトレース ID を参照できるようになり、エンドツーエンドの可視性が可能になります。 Spring Cloud Sleuth などの古いソリューションとは異なり、OpenTelemetry は軽量であり、最新の可観測性標準に準拠しています。

OpenTelemetry のプロパゲーターと Micrometer を組み合わせることで、トレース情報を使用してメトリクスとログを強化できます。可観測性ツールで、クライアント 1 とクライアント 2 の両方からのリクエストのトレースをシームレスに表示できることを想像してください。 OpenTelemetry は、Prometheus、Zipkin、Jaeger との統合を自動的にサポートし、トレースの視覚化を一元化できるようにします。このアプローチにより、カスタム ヘッダーが関係する場合でもトレース データが失われることがなくなり、デバッグが大幅に容易になります。 🚀

  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 と統合して、エンドツーエンドのトレースを視覚化できます。

最新のシステムでは、信頼性の高い可観測性のためにカスタム トレース ヘッダーの処理が重要です。フィルターとインターセプターを使用すると、クライアントが提供するトレース情報を取得し、それをサービス全体に正しく伝達できます。これにより、ログの断片化やトレースの欠落が回避されます。 🔍

Spring Boot 3.4 を Micrometer または OpenTelemetry と組み合わせると、Spring Cloud Sleuth などの古いツールに依存せずに堅牢なソリューションが可能になります。クライアント 1 の標準ヘッダーを扱う場合でも、クライアント 2 のカスタム ヘッダーを扱う場合でも、これらの手法を実装すると、トレースのギャップを効率的に埋めることができます。 🚀

  1. Spring Boot 公式ドキュメント: トレース コンテキストの伝播。 Spring Boot ドキュメント
  2. Java 開発者向け OpenTelemetry: トレース伝播のガイド。 OpenTelemetry Java
  3. マイクロメーターの可観測性に関するドキュメント: カスタム トレース ヘッダーの統合。 マイクロメーターの可観測性
  4. SLF4J ロギング API: マップされた診断コンテキスト (MDC) の使用例。 SLF4Jマニュアル