Comment utiliser Spring Boot 3.4 pour propager des traces à partir d'en-têtes personnalisés

Temp mail SuperHeros
Comment utiliser Spring Boot 3.4 pour propager des traces à partir d'en-têtes personnalisés
Comment utiliser Spring Boot 3.4 pour propager des traces à partir d'en-têtes personnalisés

Gestion des traces d'en-tête personnalisées dans Spring Boot 3.4

Imaginez que vous disposez d'un service Web Spring Boot 3.4 fonctionnant de manière transparente avec deux clients. Le premier client utilise Spring Boot 3+, ce qui facilite la propagation des traces. Sans aucun effort supplémentaire, vous obtenez une belle continuité de trace de bout en bout 🪄. Les journaux semblent propres et connectés, comme par magie.

Cependant, les choses changent lorsque le client deux entre en jeu. Au lieu des en-têtes de traçage standard, ils envoient des en-têtes personnalisés comme « ot-custom-traceid » et « ot-custom-spanid ». Bien que ces en-têtes personnalisés contiennent des informations de trace valides, Spring Boot ne parvient pas à propager ces traces. Le résultat ? Vous perdez la possibilité de connecter les traces client aux journaux côté serveur.

Cela crée un écart d’observabilité. Pour le premier client, vous voyez le chemin complet d’une demande à travers les services. Pour le client deux, vous ne voyez que les journaux côté serveur, il manque la trace client critique. C’est comme voir la moitié d’un puzzle : vous savez qu’il manque quelque chose mais vous n’arrivez pas à assembler les pièces. 😓

Dans cet article, nous explorerons comment résoudre ce problème sans compter sur Spring Cloud Sleuth, en restant fidèle à l'écosystème Spring Boot 3.4. À la fin, vous saurez comment propager et poursuivre les traces à partir des en-têtes personnalisés, garantissant ainsi une observabilité transparente sur votre système.

Commande Exemple d'utilisation
MDC.put Cette commande ajoute des paires clé-valeur au Mapped Diagnostic Context (MDC), permettant d'inclure des ID de trace personnalisés dans les journaux. Par exemple, MDC.put("traceId", "12345").
MDC.clear Efface toutes les entrées du MDC après le traitement d’une demande pour éviter toute trace de contamination entre les demandes. Par exemple, MDC.clear().
OncePerRequestFilter Un filtre Spring Boot qui garantit que la logique du filtre n'est exécutée qu'une seule fois par requête HTTP, idéal pour le traçage des en-têtes. Exemple : la classe publique CustomTraceFilter étend OncePerRequestFilter.
filterChain.doFilter Passe au filtre suivant de la chaîne, garantissant que la demande continue via d'autres filtres. Par exemple, filterChain.doFilter(requête, réponse).
RestTemplate.getInterceptors() Récupère la liste des intercepteurs pour une instance RestTemplate, permettant d'ajouter des intercepteurs personnalisés. Exemple : restTemplate.getInterceptors().add(new CustomInterceptor()).
ClientHttpRequestInterceptor Une interface pour intercepter les requêtes HTTP sortantes et ajouter des en-têtes personnalisés. Par exemple, implémenter ClientHttpRequestInterceptor pour insérer des ID de trace.
HttpServletRequest.getHeader Extrait la valeur d'un en-tête HTTP spécifique de la requête entrante. Exemple : request.getHeader("ot-custom-traceid").
FilterRegistrationBean Enregistre les filtres personnalisés dans l'application Spring Boot. Par exemple : RegistrationBean.setFilter(new CustomTraceFilter()).
MockMvc.perform Simule les requêtes HTTP dans les tests unitaires pour les applications Spring Boot. Exemple : mockMvc.perform(get("/test-endpoint").header("ot-custom-traceid", "12345")).
ClientHttpRequestExecution.execute Exécute la requête HTTP interceptée avec le corps de la requête et les en-têtes fournis. Exemple : exécution.execute(requête, corps).

Propagation de la trace d'en-tête personnalisée dans Spring Boot

L'un des composants clés pour résoudre ce problème est le CustomTraceFilter. Ce filtre étend la OncePerRequestFilter classe, garantissant que la logique d’en-tête de trace ne s’exécute qu’une seule fois pour chaque requête HTTP. Les filtres de Spring Boot sont incroyablement utiles lors de la modification globale de requêtes ou de réponses. Par exemple, si le client envoie des informations de traçage telles que ot-custom-traceid ou ot-custom-spanid dans les en-têtes personnalisés, ce filtre intercepte la requête, extrait ces en-têtes et les propage dans le Mapped Diagnostic Context (MDC). En ajoutant les ID de trace au MDC, nous garantissons que ces identifiants sont visibles dans les journaux générés lors du traitement des demandes.

Le MDC est un élément essentiel des frameworks de journalisation tels que SLF4J et Logback. Cela nous permet de stocker des informations contextuelles pour le thread actuel, telles que des ID de trace personnalisés. Utiliser des commandes comme MDC.put et MDC.clear, nous veillons à ce que le système de journalisation inclut les détails de la trace et évite toute contamination entre demandes concurrentes. Par exemple, si le client deux envoie « ot-custom-traceid » sous la forme « 8f7ebd8a73f9a8f50e6a00a87a20952a », cet ID est stocké dans MDC et inclus dans tous les journaux en aval, créant ainsi un chemin de trace cohérent.

En revanche, pour les requêtes HTTP sortantes, l'intercepteur RestTemplate joue un rôle essentiel. En mettant en œuvre ClientHttpRequestInterceptor, nous pouvons attacher les mêmes en-têtes de trace (`ot-custom-traceid` et `ot-custom-spanid`) aux requêtes sortantes. Cela garantit que la continuité de la trace est maintenue lorsque l'application appelle d'autres microservices. Par exemple, lorsque le serveur traite une requête avec l'ID de trace « 8f7ebd8a73f9a8f50e6a00a87a20952a », il attache cet ID aux en-têtes sortants, afin que les services en aval puissent reconnaître et propager la trace de manière transparente.

Enfin, les tests unitaires écrits avec MockMvc valident l'ensemble de la configuration en simulant les requêtes HTTP et en vérifiant la propagation des en-têtes. Dans les applications réelles, les tests sont cruciaux pour garantir que les en-têtes de trace sont correctement gérés. Par exemple, en envoyant une requête GET avec des en-têtes personnalisés et en inspectant la réponse ou les journaux, nous pouvons confirmer que le filtre et l'intercepteur fonctionnent comme prévu. Cette approche globale résout le défi sans s'appuyer sur des dépendances héritées comme Spring Cloud Sleuth. En fin de compte, la combinaison de filtres, d'intercepteurs et de MDC garantit la continuité des traces même lorsque les clients utilisent des en-têtes personnalisés, ce qui rend le système robuste et entièrement observable. 🌟

Propagation des en-têtes de traçage personnalisés dans Spring Boot 3.4

Utilisation de Java avec Spring Boot 3.4 et Micrometer pour le traitement backend

// 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;
    }
}

Test unitaire pour la propagation des en-têtes de trace personnalisés

Tests avec JUnit et MockMvc pour valider la propagation de l'en-tête de trace

// 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());
    }
}

Propagation d'en-têtes personnalisés dans les requêtes HTTP à l'aide de RestTemplate

Utilisation des intercepteurs RestTemplate pour ajouter des en-têtes personnalisés dans les requêtes sortantes

// 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;
    }
}

Gestion des traces d'en-tête personnalisées avec OpenTelemetry dans Spring Boot 3.4

Lorsque vous travaillez avec Spring Boot 3.4, une autre approche puissante pour propager les traces à partir des en-têtes personnalisés consiste à intégrer OpenTélémétrie. OpenTelemetry, un framework d'observabilité open source, permet d'instrumenter, de collecter et d'exporter des traces de manière transparente. Il fournit des mécanismes pour extraire et injecter le contexte de trace, y compris des en-têtes personnalisés tels que ot-custom-traceid et ot-custom-spanid, dans votre candidature. En tirant parti du TextMapPropagator d'OpenTelemetry, vous pouvez combler le fossé entre les clients non standard et votre système d'observabilité.

Pour utiliser OpenTelemetry dans Spring Boot 3.4, un propagateur personnalisé peut être implémenté pour extraire les informations de traçage des en-têtes personnalisés et les attacher au contexte de trace actuel. Par exemple, lorsque votre serveur reçoit une requête entrante du client deux, OpenTelemetry peut analyser les en-têtes personnalisés et reconstruire le contexte de trace d'origine. Cela garantit que les services en aval voient les mêmes ID de trace, permettant une visibilité de bout en bout. Contrairement aux anciennes solutions telles que Spring Cloud Sleuth, OpenTelemetry est légère et s'aligne sur les normes d'observabilité modernes.

En combinant le propagateur d'OpenTelemetry avec Micrometer, vous pouvez enrichir vos métriques et votre journalisation avec des informations de trace. Imaginez voir les traces des demandes provenant à la fois du Client Un et du Client Deux de manière transparente dans votre outil d'observabilité. OpenTelemetry prend automatiquement en charge les intégrations avec Prometheus, Zipkin ou Jaeger, vous permettant de centraliser la visualisation des traces. Cette approche garantit que même lorsque des en-têtes personnalisés sont impliqués, aucune donnée de trace n'est perdue et le débogage devient considérablement plus facile. 🚀

Questions fréquemment posées sur la propagation de traces personnalisées dans Spring Boot

  1. Comment extraire manuellement les en-têtes de trace personnalisés dans Spring Boot ?
  2. Vous pouvez utiliser request.getHeader("custom-header") pour récupérer manuellement un en-tête spécifique et l'ajouter au MDC à l'aide de MDC.put("traceId", value).
  3. Quel est l’avantage d’utiliser OpenTelemetry pour la propagation de traces personnalisées ?
  4. OpenTelemetry offre une approche moderne et indépendante du fournisseur pour propager les traces, y compris les en-têtes personnalisés, entre les microservices.
  5. Puis-je propager des en-têtes personnalisés avec RestTemplate dans Spring Boot ?
  6. Oui, en implémentant un ClientHttpRequestInterceptor, vous pouvez attacher des en-têtes personnalisés tels que traceid et spanid aux requêtes sortantes.
  7. Comment enregistrer un filtre pour capturer les en-têtes globalement ?
  8. Vous pouvez créer un filtre qui étend OncePerRequestFilter et l'enregistrer à l'aide de FilterRegistrationBean pour capturer les en-têtes de tous les points de terminaison.
  9. Quels outils puis-je utiliser pour visualiser les traces de Spring Boot ?
  10. Des outils tels que Zipkin, Jaeger et Prometheus peuvent s'intégrer à Spring Boot et OpenTelemetry pour visualiser des traces de bout en bout.

Assurer une continuité de trace transparente

Dans les systèmes modernes, la gestion des en-têtes de trace personnalisés est essentielle pour une observabilité fiable. En utilisant des filtres et des intercepteurs, vous pouvez capturer les informations de traçage fournies par le client et les propager correctement dans vos services. Cela évite les journaux fragmentés et les traces manquantes. 🔍

Spring Boot 3.4, combiné à Micrometer ou OpenTelemetry, permet des solutions robustes sans s'appuyer sur des outils plus anciens comme Spring Cloud Sleuth. Que vous ayez affaire aux en-têtes standard du Client One ou aux en-têtes personnalisés du Client Two, la mise en œuvre de ces techniques comble efficacement les lacunes de trace. 🚀

Sources et références
  1. Documentation officielle de Spring Boot : propagation des contextes de traçage. Documentation de démarrage de printemps
  2. OpenTelemetry pour les développeurs Java : Guide de propagation des traces. OpenTélémétrie Java
  3. Documentation sur l'observabilité des micromètres : intégration d'en-têtes de trace personnalisés. Observabilité du micromètre
  4. API de journalisation SLF4J : cas d'utilisation du contexte de diagnostic mappé (MDC). Manuel SLF4J