Como usar Spring Boot 3.4 para propagar rastreamentos de cabeçalhos personalizados

Temp mail SuperHeros
Como usar Spring Boot 3.4 para propagar rastreamentos de cabeçalhos personalizados
Como usar Spring Boot 3.4 para propagar rastreamentos de cabeçalhos personalizados

Tratamento de rastreamentos de cabeçalho personalizados no Spring Boot 3.4

Imagine que você tem um serviço web Spring Boot 3.4 funcionando perfeitamente com dois clientes. O primeiro cliente usa Spring Boot 3+, facilitando a propagação do rastreamento. Sem nenhum esforço extra, você obtém uma bela continuidade de rastreamento de ponta a ponta 🪄. Os registros parecem limpos e conectados, como num passe de mágica.

No entanto, as coisas mudam quando o cliente dois entra em jogo. Em vez de cabeçalhos de rastreamento padrão, eles enviam cabeçalhos personalizados como `ot-custom-traceid` e `ot-custom-spanid`. Embora esses cabeçalhos personalizados contenham informações de rastreamento válidas, o Spring Boot falha ao propagar esses rastreamentos. O resultado? Você perde a capacidade de conectar rastreios de cliente com logs do lado do servidor.

Isso cria uma lacuna de observabilidade. Para o cliente um, você vê o caminho completo de uma solicitação entre serviços. Para o cliente dois, você vê apenas logs do lado do servidor, perdendo o rastreamento crítico do cliente. É como ver metade de um quebra-cabeça: você sabe que algo está faltando, mas não consegue juntar as peças. 😓

Neste artigo, exploraremos como resolver esse problema sem depender do Spring Cloud Sleuth, permanecendo fiéis ao ecossistema Spring Boot 3.4. Ao final, você saberá como propagar e continuar os rastreamentos de cabeçalhos personalizados, garantindo uma observabilidade perfeita em todo o seu sistema.

Comando Exemplo de uso
MDC.put Este comando adiciona pares de valores-chave ao Mapped Diagnostic Context (MDC), permitindo que IDs de rastreamento personalizados sejam incluídos nos logs. Por exemplo, MDC.put("traceId", "12345").
MDC.clear Limpa todas as entradas do MDC depois que uma solicitação é processada para evitar contaminação de rastros entre solicitações. Por exemplo, MDC.clear().
OncePerRequestFilter Um filtro Spring Boot que garante que a lógica do filtro seja executada apenas uma vez por solicitação HTTP, ideal para rastrear cabeçalhos. Exemplo: a classe pública CustomTraceFilter estende OncePerRequestFilter.
filterChain.doFilter Prossegue para o próximo filtro na cadeia, garantindo que a solicitação continue através de outros filtros. Por exemplo, filterChain.doFilter(solicitação, resposta).
RestTemplate.getInterceptors() Recupera a lista de interceptores para uma instância RestTemplate, permitindo a adição de interceptores personalizados. Exemplo: restTemplate.getInterceptors().add(new CustomInterceptor()).
ClientHttpRequestInterceptor Uma interface para interceptar solicitações HTTP de saída e adicionar cabeçalhos personalizados. Por exemplo, implementando ClientHttpRequestInterceptor para inserir IDs de rastreamento.
HttpServletRequest.getHeader Extrai o valor de um cabeçalho HTTP específico da solicitação recebida. Exemplo: request.getHeader("ot-custom-traceid").
FilterRegistrationBean Registra filtros personalizados no aplicativo Spring Boot. Por exemplo: RegistrationBean.setFilter(new CustomTraceFilter()).
MockMvc.perform Simula solicitações HTTP em testes unitários para aplicativos Spring Boot. Exemplo: mockMvc.perform(get("/test-endpoint").header("ot-custom-traceid", "12345")).
ClientHttpRequestExecution.execute Executa a solicitação HTTP interceptada com o corpo e os cabeçalhos da solicitação fornecidos. Exemplo: execução.execute(solicitação, corpo).

Propagação de rastreamento de cabeçalho personalizado no Spring Boot

Um dos principais componentes para resolver esse problema é o CustomTraceFilter. Este filtro estende o Filtro OncePerRequest classe, garantindo que a lógica do cabeçalho de rastreamento seja executada apenas uma vez para cada solicitação HTTP. Os filtros no Spring Boot são extremamente úteis ao modificar solicitações ou respostas globalmente. Por exemplo, se o cliente enviar informações de rastreamento como ot-custom-traceid ou ot-custom-spanid em cabeçalhos personalizados, esse filtro intercepta a solicitação, extrai esses cabeçalhos e os propaga no Mapped Diagnostic Context (MDC). Ao adicionar os IDs de rastreamento ao MDC, garantimos que esses identificadores estejam visíveis nos logs gerados durante o processamento da solicitação.

O MDC é uma parte crítica de estruturas de registro como SLF4J e Logback. Ele nos permite armazenar informações contextuais do thread atual, como IDs de rastreamento personalizados. Usando comandos como MDC.put e MDC.claro, garantimos que o sistema de registro inclua os detalhes do rastreamento e evite a contaminação entre solicitações simultâneas. Por exemplo, se o Cliente Dois enviar `ot-custom-traceid` como `8f7ebd8a73f9a8f50e6a00a87a20952a`, esse ID será armazenado no MDC e incluído em todos os logs downstream, criando um caminho de rastreamento consistente.

Por outro lado, para solicitações HTTP de saída, o interceptor RestTemplate desempenha um papel essencial. Ao implementar ClienteHttpRequestInterceptor, podemos anexar os mesmos cabeçalhos de rastreamento (`ot-custom-traceid` e `ot-custom-spanid`) às solicitações de saída. Isso garante que a continuidade do rastreamento seja mantida quando o aplicativo chamar outros microsserviços. Por exemplo, quando o servidor processa uma solicitação com ID de rastreamento `8f7ebd8a73f9a8f50e6a00a87a20952a`, ele anexa esse ID aos cabeçalhos de saída, para que os serviços downstream possam reconhecer e propagar o rastreamento perfeitamente.

Por fim, os testes de unidade escritos com MockMvc validam toda a configuração simulando solicitações HTTP e verificando a propagação do cabeçalho. Em aplicações do mundo real, os testes são cruciais para garantir que os cabeçalhos de rastreamento sejam tratados corretamente. Por exemplo, enviando uma solicitação GET com cabeçalhos personalizados e inspecionando a resposta ou os logs, podemos confirmar se o filtro e o interceptador funcionam conforme o esperado. Essa abordagem abrangente resolve o desafio sem depender de dependências legadas como Spring Cloud Sleuth. Em última análise, a combinação de filtros, interceptores e MDC garante a continuidade do rastreamento mesmo quando os clientes usam cabeçalhos personalizados, tornando o sistema robusto e totalmente observável. 🌟

Propagando cabeçalhos de rastreamento personalizados no Spring Boot 3.4

Usando Java com Spring Boot 3.4 e Micrometer para processamento de back-end

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

Teste de unidade para propagação de cabeçalho de rastreamento personalizado

Teste com JUnit e MockMvc para validar a propagação do cabeçalho de rastreamento

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

Propagando cabeçalhos personalizados em solicitações HTTP usando RestTemplate

Usando interceptores RestTemplate para adicionar cabeçalhos personalizados em solicitações de saída

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

Tratamento de rastreamentos de cabeçalho personalizados com OpenTelemetry no Spring Boot 3.4

Ao trabalhar com o Spring Boot 3.4, outra abordagem poderosa para propagar rastreamentos de cabeçalhos personalizados é integrar OpenTelemetria. OpenTelemetry, uma estrutura de observabilidade de código aberto, ajuda a instrumentar, coletar e exportar rastreamentos de maneira integrada. Ele fornece mecanismos para extrair e injetar contexto de rastreamento, incluindo cabeçalhos personalizados como ot-custom-traceid e ot-custom-spanid, em seu aplicativo. Ao aproveitar o TextMapPropagator do OpenTelemetry, você pode preencher a lacuna entre clientes não padrão e seu sistema de observabilidade.

Para usar o OpenTelemetry no Spring Boot 3.4, um propagador personalizado pode ser implementado para extrair informações de rastreamento dos cabeçalhos personalizados e anexá-las ao contexto de rastreamento atual. Por exemplo, quando o seu servidor recebe uma solicitação do Cliente Dois, o OpenTelemetry pode analisar cabeçalhos personalizados e reconstruir o contexto de rastreamento original. Isso garante que os serviços downstream vejam os mesmos IDs de rastreamento, permitindo visibilidade de ponta a ponta. Ao contrário de soluções mais antigas, como o Spring Cloud Sleuth, o OpenTelemetry é leve e está alinhado aos padrões modernos de observabilidade.

Ao combinar o propagador do OpenTelemetry com o Micrometer, você pode enriquecer suas métricas e registros com informações de rastreamento. Imagine ver rastreamentos de solicitações provenientes do Cliente Um e do Cliente Dois perfeitamente em sua ferramenta de observabilidade. OpenTelemetry suporta automaticamente integrações com Prometheus, Zipkin ou Jaeger, permitindo centralizar a visualização de rastreamento. Essa abordagem garante que, mesmo quando cabeçalhos personalizados estão envolvidos, nenhum dado de rastreamento seja perdido e a depuração se torne significativamente mais fácil. 🚀

Perguntas frequentes sobre a propagação de rastreamentos personalizados no Spring Boot

  1. Como extraio manualmente cabeçalhos de rastreamento personalizados no Spring Boot?
  2. Você pode usar request.getHeader("custom-header") para buscar manualmente um cabeçalho específico e adicioná-lo ao MDC usando MDC.put("traceId", value).
  3. Qual é a vantagem de usar o OpenTelemetry para propagação de rastreamento customizada?
  4. OpenTelemetry fornece uma abordagem moderna e neutra em termos de fornecedor para propagação de rastreamentos, incluindo cabeçalhos personalizados, em microsserviços.
  5. Posso propagar cabeçalhos personalizados com RestTemplate no Spring Boot?
  6. Sim, ao implementar um ClientHttpRequestInterceptor, você pode anexar cabeçalhos personalizados como traceid e spanid a solicitações de saída.
  7. Como registrar um filtro para capturar cabeçalhos globalmente?
  8. Você pode criar um filtro que estenda OncePerRequestFilter e registrá-lo usando FilterRegistrationBean para capturar cabeçalhos para todos os endpoints.
  9. Quais ferramentas posso usar para visualizar rastros do Spring Boot?
  10. Ferramentas como Zipkin, Jaeger e Prometheus podem ser integradas ao Spring Boot e OpenTelemetry para visualizar rastreamentos de ponta a ponta.

Garantindo a continuidade contínua do rastreamento

Em sistemas modernos, o tratamento de cabeçalhos de rastreamento personalizados é fundamental para uma observabilidade confiável. Ao usar filtros e interceptores, você pode capturar informações de rastreamento fornecidas pelo cliente e propagá-las corretamente em seus serviços. Isso evita logs fragmentados e rastreios ausentes. 🔍

Spring Boot 3.4, combinado com Micrometer ou OpenTelemetry, permite soluções robustas sem depender de ferramentas mais antigas como Spring Cloud Sleuth. Esteja você lidando com cabeçalhos padrão do Cliente Um ou cabeçalhos personalizados do Cliente Dois, a implementação dessas técnicas preenche as lacunas de rastreamento de forma eficiente. 🚀

Fontes e Referências
  1. Documentação oficial do Spring Boot: Propagação de contextos de rastreamento. Documentação de inicialização Spring
  2. OpenTelemetry para desenvolvedores Java: guia para propagação de rastreamento. OpenTelemetria Java
  3. Documentação de observabilidade do micrômetro: Integrando cabeçalhos de rastreamento personalizados. Observabilidade Micrômetro
  4. API de registro SLF4J: casos de uso de contexto de diagnóstico mapeado (MDC). Manual SLF4J