paint-brush
Rastreamento OpenTelemetry no Spring Boot: Escolhendo entre Java Agent e Micrômetro parapor@nfrankel
205 leituras

Rastreamento OpenTelemetry no Spring Boot: Escolhendo entre Java Agent e Micrômetro para

por Nicolas Fränkel8m2024/08/11
Read on Terminal Reader

Muito longo; Para ler

Esta publicação compara soluções de rastreamento OpenTelemetry em um aplicativo Spring Boot usando Java Agent v1, Java Agent v2 e Micrometer Tracing. Ela destaca diferenças em configuração, funcionalidade e recursos de rastreamento, oferecendo insights sobre a eficácia e aplicação de cada abordagem para observabilidade.
featured image - Rastreamento OpenTelemetry no Spring Boot: Escolhendo entre Java Agent e Micrômetro para
Nicolas Fränkel HackerNoon profile picture

Minha demonstração do OpenTelemetry Tracing apresenta dois componentes do Spring Boot. Um usa o agente Java, e notei um comportamento diferente quando o atualizei recentemente da v1.x para a v2.x. No outro, estou usando o Micrometer Tracing porque compilo para o GraalVM nativo, e ele não consegue processar agentes Java.



Nesta postagem, quero comparar essas três abordagens: Java agent v1, Java agent v2 e Micrometer Tracing.

A aplicação base e sua infraestrutura

Usarei o mesmo aplicativo base: um aplicativo Spring Boot simples, codificado em Kotlin. Ele oferece um único endpoint.


  • A função além do ponto final é chamada entry()
  • Ele chama outra função chamada intermediate()
  • Este último usa uma instância WebClient , a substituição de RestTemplate , para fazer uma chamada para o ponto de extremidade acima
  • Para evitar loop infinito, passo um cabeçalho de solicitação personalizado: se a função entry() o encontrar, ela não prossegue.


Diagrama de sequência do aplicativo de exemplo


Isso se traduz no seguinte código:


 @SpringBootApplication class Agent1xApplication @RestController class MicrometerController { private val logger = LoggerFactory.getLogger(MicrometerController::class.java) @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") if (done == null) intermediate() } fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() } }


Para cada configuração, verificarei dois estágios: o estágio primário, com o OpenTelemetry habilitado, e um estágio de personalização para criar intervalos internos adicionais.

Rastreamento de micrômetro

O Micrometer Tracing deriva do Micrometer , uma "fachada de observabilidade de aplicativos independente de fornecedor".


O Micrometer Tracing fornece uma fachada simples para as bibliotecas de rastreadores mais populares, permitindo que você instrumente seu código de aplicativo baseado em JVM sem bloqueio de fornecedor. Ele foi projetado para adicionar pouca ou nenhuma sobrecarga à sua atividade de coleta de rastreamento, ao mesmo tempo em que maximiza a portabilidade do seu esforço de rastreamento.


-- Site de rastreamento de micrômetro


Para começar com o Micrometer Tracing, é necessário adicionar algumas dependências:


  • Atuador de inicialização de mola, org.springframework.boot:spring-boot-starter-actuator
  • Rastreamento de micrômetro em si, io.micrometer:micrometer-tracing
  • Uma "ponte" para a API de backend de rastreamento de alvos. No meu caso, é OpenTelemetry, portanto io.micrometer:micrometer-tracing-bridge-otel
  • Um exportador concreto para o backend, io.opentelemetry:opentelemetry-exporter-otlp


Não precisamos de uma BOM porque as versões já estão definidas no pai do Spring Boot.


No entanto, precisamos de dois parâmetros de configuração de tempo de execução: para onde os traces devem ser enviados e qual é o nome do componente. Eles são governados pelas variáveis MANAGEMENT_OTLP_TRACING_ENDPOINT e SPRING_APPLICATION_NAME .


 services: jaeger: image: jaegertracing/all-in-one:1.55 environment: - COLLECTOR_OTLP_ENABLED=true #1 ports: - "16686:16686" micrometer-tracing: build: dockerfile: Dockerfile-micrometer environment: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://jaeger:4318/v1/traces #2 SPRING_APPLICATION_NAME: micrometer-tracing #3
  1. Habilitar o coletor OpenTelemetry para Jaeger
  2. URL completa para o ponto de extremidade gRPC do Jaeger OpenTelemetry
  3. Defina o nome do serviço do OpenTelemetry


Aqui está o resultado:

Traços micrométricos no Jaeger sem personalização


Sem nenhuma personalização, o Micrometer cria intervalos ao receber e enviar solicitações HTTP.


O framework precisa injetar mágica no RestClient para envio. Devemos deixar o primeiro instanciar o último para isso:


 @SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }


Podemos criar spans manuais de várias maneiras, uma por meio da própria API OpenTelemetry. No entanto, a configuração requer muito código boilerplate. A maneira mais direta é a API de Observação do Micrometer. Seu principal benefício é usar uma única API que gerencia métricas e traces .


Diagrama de classe da API de observação

Aqui está o código atualizado:


 class MicrometerController( private val restClient: RestClient, private val registry: ObservationRegistry ) { @GetMapping("/{message}") fun entry(@PathVariable message: String, @RequestHeader("X-done") done: String?) { logger.info("entry: $message") val observation = Observation.start("entry", registry) if (done == null) intermediate(observation) observation.stop() } fun intermediate(parent: Observation) { logger.info("intermediate") val observation = Observation.createNotStarted("intermediate", registry) .parentObservation(parent) .start() restClient.get() .header("X-done", "true") .retrieve() .toBodilessEntity() observation.stop() } }


As chamadas de observação adicionadas refletem nos traços gerados:


Rastros micrométricos em Jaeger com a API de observação

Agente OpenTelemetry v1

Uma alternativa ao Micrometer Tracing é o OpenTelemetry Java Agent genérico. Seu principal benefício é que ele não impacta nem o código nem os desenvolvedores; o agente é uma preocupação puramente de escopo de tempo de execução.


 java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar


O agente obedece à configuração do OpenTelemetry com variáveis de ambiente:


 services: agent-1x: build: dockerfile: Dockerfile-agent1 environment: OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4317 #1 OTEL_RESOURCE_ATTRIBUTES: service.name=agent-1x #2 OTEL_METRICS_EXPORTER: none #3 OTEL_LOGS_EXPORTER: none #4 ports: - "8081:8080"
  1. Defina o protocolo, o domínio e a porta. A biblioteca anexa /v1/traces
  2. Defina o nome do serviço do OpenTelemetry
  3. Não exporte nem as métricas nem os logs


Sem mais configurações, obtemos os seguintes rastros:


Rastreamentos do agente v1 no Jaeger sem personalização


O agente rastreia automaticamente as solicitações, tanto recebidas quanto enviadas, bem como as funções marcadas com anotações relacionadas ao Spring . Os rastreamentos são corretamente aninhados uns dentro dos outros, de acordo com a pilha de chamadas. Para rastrear funções adicionais, precisamos adicionar uma dependência à nossa base de código, io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations . Agora podemos anotar funções não rastreadas anteriormente com a anotação @WithSpan .

Diagrama de classe @WithSpan


A parte value() governa o rótulo do trace, enquanto o kind é traduzido como um atributo span.kind . Se o value for definido como uma string vazia, que é o padrão, ele emite o nome da função. Para meus propósitos, valores padrão são bons o suficiente.


 @WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }


Ele produz o novo rastreamento esperado intermediate() :

Rastreamentos do agente v1 no Jaeger com anotações

Agente OpenTelemetry v2

A OpenTelemetry lançou uma nova versão principal do agente em janeiro deste ano. Atualizei minha demo com ela; os traces agora são criados somente quando o aplicativo recebe e envia solicitações.

Rastreamentos do agente v2 no Jaeger sem personalização


Quanto à versão anterior, podemos adicionar traces com a anotação @WithSpan . A única diferença é que também precisamos anotar a função entry() . Ela não é rastreada por padrão.

Rastreamentos do agente v2 no Jaeger com anotações


Discussão

O Spring se tornou bem-sucedido por dois motivos: ele simplificou soluções complexas, ou seja , EJBs 2, e forneceu uma camada de abstração sobre bibliotecas concorrentes. O Micrometer Tracing começou como uma camada de abstração sobre Zipkin e Jaeger, e fazia todo o sentido. Este argumento se torna discutível com o OpenTelemetry sendo suportado pela maioria das bibliotecas em linguagens de programação e coletores de rastreamento. A API de observação ainda é um benefício considerável do Micrometer Tracing, pois usa uma única API sobre Métricas e Rastreamentos.


No lado do Java Agent, a configuração do OpenTelemetry é similar em todas as pilhas de tecnologia e bibliotecas - variáveis de ambiente. Fiquei um pouco decepcionado quando atualizei da v1 para a v2, pois o novo agente não é compatível com Spring: funções anotadas com Spring não são rastreadas por padrão.


No final, é uma decisão sábia. É muito melhor ser explícito sobre os spans que você quer do que remover alguns que você não quer ver.


Obrigado a Jonatan Ivanov por sua ajuda e sua avaliação .


O código-fonte completo desta postagem pode ser encontrado no GitHub:

Para ir mais longe:


Originalmente publicado em A Java Geek em 3 de agosto de 2024