paint-brush
Seguimiento de OpenTelemetry en Spring Boot: elección entre Java Agent y Micrometer parapor@nfrankel
193 lecturas

Seguimiento de OpenTelemetry en Spring Boot: elección entre Java Agent y Micrometer para

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

Demasiado Largo; Para Leer

En esta publicación se comparan las soluciones de rastreo de OpenTelemetry en una aplicación Spring Boot que utiliza Java Agent v1, Java Agent v2 y Micrometer Tracing. Se destacan las diferencias en la configuración, la funcionalidad y las capacidades de rastreo, y se ofrece información sobre la eficacia y la aplicación de cada enfoque para la observabilidad.
featured image - Seguimiento de OpenTelemetry en Spring Boot: elección entre Java Agent y Micrometer para
Nicolas Fränkel HackerNoon profile picture

Mi demostración de OpenTelemetry Tracing incluye dos componentes de Spring Boot. Uno usa el agente Java y noté un comportamiento diferente cuando lo actualicé recientemente de v1.x a v2.x. En el otro, uso Micrometer Tracing porque compilo en GraalVM nativo y no puede procesar agentes Java.



En esta publicación, quiero comparar estos tres enfoques: agente Java v1, agente Java v2 y Micrometer Tracing.

La aplicación base y su infraestructura

Usaré la misma aplicación base: una aplicación Spring Boot sencilla, codificada en Kotlin. Ofrece un único punto de conexión.


  • La función más allá del punto final se llama entry()
  • Llama a otra función llamada intermediate()
  • Este último utiliza una instancia WebClient , el reemplazo de RestTemplate , para realizar una llamada al punto final anterior.
  • Para evitar bucles infinitos, paso un encabezado de solicitud personalizado: si la función entry() lo encuentra, no continúa.


Diagrama de secuencia de aplicación de muestra


Se traduce al siguiente 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 configuración, comprobaré dos etapas: la etapa principal, con OpenTelemetry habilitado, y una etapa de personalización para crear tramos internos adicionales.

Trazado micrométrico

Micrometer Tracing proviene de Micrometer , una "fachada de observabilidad de aplicaciones independiente del proveedor".


Micrometer Tracing ofrece una interfaz sencilla para las bibliotecas de trazadores más populares, lo que le permite instrumentar el código de su aplicación basada en JVM sin depender de un proveedor. Está diseñado para agregar poca o ninguna sobrecarga a su actividad de recopilación de trazadores y, al mismo tiempo, maximizar la portabilidad de su esfuerzo de trazado.


-- Sitio de rastreo de micrómetros


Para comenzar con el seguimiento micrométrico, es necesario agregar algunas dependencias:


  • Actuador de arranque de resorte, org.springframework.boot:spring-boot-starter-actuator
  • Trazado del micrómetro en sí, io.micrometer:micrometer-tracing
  • Un "puente" hacia la API de backend de seguimiento de destino. En mi caso, es OpenTelemetry, de ahí io.micrometer:micrometer-tracing-bridge-otel
  • Un exportador concreto al backend, io.opentelemetry:opentelemetry-exporter-otlp


No necesitamos una lista de materiales porque las versiones ya están definidas en el elemento principal de Spring Boot.


Sin embargo, necesitamos dos parámetros de configuración en tiempo de ejecución: dónde se deben enviar los seguimientos y cuál es el nombre del componente. Estos parámetros se controlan mediante las variables MANAGEMENT_OTLP_TRACING_ENDPOINT y 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 el recopilador OpenTelemetry para Jaeger
  2. URL completa al punto final gRPC de Jaeger OpenTelemetry
  3. Establecer el nombre del servicio de OpenTelemetry


Aquí está el resultado:

Trazas micrométricas en Jaeger sin personalización


Sin ninguna personalización, Micrometer crea intervalos al recibir y enviar solicitudes HTTP.


El marco debe inyectar magia en RestClient para realizar el envío. Debemos dejar que el primero cree una instancia del segundo para ello:


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


Podemos crear intervalos manuales de varias formas, una de ellas a través de la propia API de OpenTelemetry. Sin embargo, la configuración requiere una gran cantidad de código repetitivo. La forma más sencilla es la API de observación de Micrometer. Su principal beneficio es utilizar una única API que administra tanto las métricas como los rastros .


Diagrama de clases de la API de observación

Aquí está el código actualizado:


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


Las llamadas de observación agregadas se reflejan en los rastros generados:


Trazas de micrómetros en Jaeger con la API de observación

Agente OpenTelemetry v1

Una alternativa a Micrometer Tracing es el agente genérico OpenTelemetry Java . Su principal beneficio es que no afecta ni al código ni a los desarrolladores; el agente es una preocupación puramente de tiempo de ejecución.


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


El agente respeta la configuración de OpenTelemetry con variables de entorno:


 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. Establezca el protocolo, el dominio y el puerto. La biblioteca agrega /v1/traces
  2. Establecer el nombre del servicio de OpenTelemetry
  3. No exportar ni las métricas ni los registros


Sin más configuración obtenemos los siguientes rastros:


Rastros del agente v1 en Jaeger sin personalización


El agente rastrea automáticamente las solicitudes, tanto recibidas como enviadas, así como las funciones marcadas con anotaciones relacionadas con Spring . Los rastros se anidan correctamente uno dentro del otro, de acuerdo con la pila de llamadas. Para rastrear funciones adicionales, necesitamos agregar una dependencia a nuestra base de código, io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations . Ahora podemos anotar funciones no rastreadas previamente con la anotación @WithSpan .

Diagrama de clases @WithSpan


La parte value() controla la etiqueta del seguimiento, mientras que el kind se traduce como un atributo span.kind . Si el valor se establece en una cadena vacía, que es el valor predeterminado, se muestra el nombre de la función. Para mis propósitos, los valores predeterminados son suficientes.


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


Produce el nuevo rastro intermediate() esperado:

Rastros del agente v1 en Jaeger con anotaciones

Agente OpenTelemetry v2

OpenTelemetry lanzó una nueva versión principal del agente en enero de este año. Actualicé mi demostración con ella; ahora los rastros solo se crean cuando la aplicación recibe y envía solicitudes.

Rastros del agente v2 en Jaeger sin personalización


Al igual que en la versión anterior, podemos agregar trazas con la anotación @WithSpan . La única diferencia es que también debemos anotar la función entry() . No se traza de forma predeterminada.

Rastros del agente v2 en Jaeger con anotaciones


Discusión

Spring tuvo éxito por dos razones: simplificó soluciones complejas, es decir , EJBs 2, y proporcionó una capa de abstracción sobre las bibliotecas de la competencia. Micrometer Tracing comenzó como una capa de abstracción sobre Zipkin y Jaeger, y tenía todo el sentido. Este argumento se vuelve discutible cuando OpenTelemetry es compatible con la mayoría de las bibliotecas en todos los lenguajes de programación y recopiladores de seguimiento. La API de observación sigue siendo un beneficio considerable de Micrometer Tracing, ya que utiliza una única API sobre Metrics y Traces.


Del lado del agente de Java, la configuración de OpenTelemetry es similar en todas las bibliotecas y pilas de tecnología: variables de entorno. Me decepcioné un poco cuando actualicé de la versión 1 a la versión 2, ya que el nuevo agente no es compatible con Spring: las funciones anotadas con Spring no se rastrean de forma predeterminada.


Al final, es una decisión acertada. Es mucho mejor ser explícito sobre los intervalos que quieres que eliminar algunos que no quieres ver.


Gracias a Jonatan Ivanov por su ayuda y su reseña .


El código fuente completo de esta publicación se puede encontrar en GitHub:

Para ir más allá:


Publicado originalmente en A Java Geek el 3 de agosto de 2024