Documentation for a newer release is available. View Latest

Trazas y otra telemetría con OpenTelemetry

Esta página explica cómo implementar logging, tracing y métricas usando OpenTelemetry.

Habilitar OpenTelemetry

Habilitar OpenTelemetry para una aplicación consiste en los siguientes pasos:

  1. Añadir las dependencias de Lightbend Telemetry ("Cinnamon"):

    <dependency>
        <groupId>com.lightbend.cinnamon</groupId>
        <artifactId>cinnamon-opentracing-zipkin</artifactId>
    </dependency>
    <dependency>
        <groupId>com.lightbend.cinnamon</groupId>
        <artifactId>cinnamon-opentracing_${scala.version}</artifactId>
    </dependency>
    <dependency>
        <groupId>com.lightbend.cinnamon</groupId>
        <artifactId>cinnamon-agent</artifactId>
    </dependency>

    A partir de IPF 2024.2.0, se requiere una solución temporal para evitar conflictos entre versiones de librerías. Por favor añade lo siguiente al bloque <dependencyManagement> de tu pom.xml raíz para usar OTel y OpenTracing juntos:

    <dependency>
        <groupId>io.opentracing</groupId>
        <artifactId>opentracing-util</artifactId>
        <version>0.33.0</version>
    </dependency>
    <dependency>
        <groupId>io.zipkin.reporter2</groupId>
        <artifactId>zipkin-sender-urlconnection</artifactId>
        <version>2.16.3</version>
    </dependency>
    <dependency>
        <groupId>io.zipkin.reporter2</groupId>
        <artifactId>zipkin-reporter</artifactId>
        <version>2.16.3</version>
    </dependency>
    <dependency>
        <groupId>io.zipkin.zipkin2</groupId>
        <artifactId>zipkin</artifactId>
        <version>2.23.2</version>
    </dependency>
  2. Habilitar el Java Agent de Lightbend Telemetry, p. ej. en la sección CMD de la imagen Docker:

    exec java -javaagent:/${project.artifactId}/lib/com.lightbend.cinnamon-cinnamon-agent-${cinnamon.version}.jar

    NOTA: Esto ya se hace como parte del Icon Project Archetype, por lo que normalmente este paso no es necesario

  3. Habilitar los exportadores relevantes de métricas o eventos según la documentación de Lightbend Telemetry para métricas, eventos, logs.

  4. Para habilitar tracing, asegúrate de que el OpenTelemetry Collector está configurado para recibir spans formateados como OpenTracing, y sigue las instrucciones en Zipkin reporter.

Si todo funcionó correctamente, deberías ver algunas trazas en tu herramienta preferida (por ejemplo Jaeger, Zipkin, Grafana Tempo):

span example

Propagación de contexto

También es posible configurar IPF para simplemente propagar los encabezados relevantes sin crear información de tracing adicional. Por defecto, Lightbend Telemetry soporta reenviar los siguientes tipos de contexto:

Tipo Documentación para habilitar

W3C Trace Context (por defecto para OpenTelemetry)

Haz clic aquí

B3 (por Zipkin)

Haz clic aquí

AWS X-Ray

Haz clic aquí

Limitaciones

El tracing y la telemetría en general suelen ser de "código cero": simplemente habilitando el agente de Cinnamon se habilita la introspección a nivel de código para añadir los detalles de tracing relevantes.

Sin embargo, Lightbend Telemetry actualmente solo soporta Kafka y HTTP. Si, en cualquier frontera, los mensajes van por JMS (o cualquier otro protocolo que no sea Kafka o HTTP), el contexto de traza se perderá y se requerirá intervención para conectar las trazas.

Para conectar las trazas, sigue las instrucciones en Inject and Extract para extraer el SpanContext al enviar e inyectarlo al recibir mensajes. Un ejemplo usando un CorrelationService personalizado podría ser:

import com.iconsolutions.ipf.core.shared.correlation.CorrelationService;
import lombok.AllArgsConstructor;

@AllArgsConstructor
public class OpenTracingAwareCorrelationService implements CorrelationService {

    private final CorrelationService delegate;

    @Override
    public CompletionStage<Correlation> save(Correlation correlation) {
        var map = new HashMap<String, String>();
        GlobalTracer.get().inject(ActiveSpan.getContext(), Format.Builtin.TEXT_MAP, new TextMapAdapter(map));
        correlation.getSupportingContext().mergedWith(SupportingContext.of(map));
        return delegate.save(correlation);
    }

    @Override
    public CompletionStage<Optional<Correlation>> findByCorrelationId(CorrelationId correlationId) {
        return delegate.findByCorrelationId(correlationId)
                .thenApply(maybeCorrelation -> {
                    maybeCorrelation.ifPresent(it -> GlobalTracer.get().extract(Format.Builtin.TEXT_MAP, new TextMapAdapter(it.getSupportingContext().getMetaData().getValues())));
                    return maybeCorrelation;
                });
    }
}

Esta implementación de CorrelationService guardará el Span activo de OpenTracing en el almacén de correlaciones cuando sea posible y lo extraerá cuando sea posible.

Ten en cuenta que esto requerirá introducir una dependencia en OpenTracing, que es una librería deprecada a favor de OpenTelemetry. Esta página se actualizará cuando Lightbend Telemetry se actualice para soportar tracing nativo de OpenTelemetry.

Soporte de Lightbend Telemetry ("Cinnamon") para tracing

Lightbend Telemetry ("Cinnamon") actualmente no soporta el estándar OpenTelemetry para trazas y solo implementa la API legada OpenTracing. Sin embargo, el collector de OpenTelemetry y AWS X-Ray soportan el formato legado Zipkin de OpenTracing. Por tanto, no deberías necesitar hacer nada más para que las trazas funcionen en OpenTelemetry.

No debería ser necesaria configuración adicional aparte de añadir las librerías en el paso 1, pero haz clic aquí para ver qué configuración se puede ajustar en la librería.

Combinando OpenTelemetry y OpenTracing

Puede que tengas el requisito de propagar el contexto de traza entre OpenTelemetry y OpenTracing. Por ejemplo, si aplica la siguiente combinación:

  • Estás usando el OpenTelemetry Java Agent

  • Estás iniciando un flujo desde un @RestController, @KafkaListener, @JmsListener de Spring, o similar

  • Estás recibiendo un contexto de traza en forma de cabeceras desde un sistema o broker ascendente

Por defecto, la traza se romperá entre el lado de Spring y el lado de IPF, porque Lightbend Telemetry (y por extensión Akka e IPF) usa la API de OpenTracing, y no será consciente del contexto de traza de OTel que fue poblado por Spring. El contexto necesita propagarse manualmente de OTel a OpenTracing; sigue estos pasos para configurar tu aplicación y conectar el contexto de traza entre OTel y OpenTracing:

  1. Añade primero el agente de Lightbend Telemetry y en segundo lugar el agente de OTel:

    -javaagent:/path/to/cinnamon-agent-2.20.1.jar
    -javaagent:/path/to/opentelemetry-javaagent.jar
  2. Deshabilita la instrumentación de OTel para Akka y Akka HTTP con propiedades del sistema (o variables de entorno, o entradas de archivo de configuración):

    otel.instrumentation.akka-http.enabled=false
    otel.instrumentation.akka.enabled=false
  3. Asegúrate de que los valores de otel.service.name y cinnamon.application sean los mismos, para que las trazas parezcan provenir de la misma aplicación IPF (a menos que quieras que sean diferentes)

    Valores por defecto:

    • cinnamon.application: nombre cualificado del punto donde se inició la JVM en minúsculas, p. ej. com.mycorp.application

    • otel.service.name: unknown_service:java

  4. Al pasar del consumidor de mensajes de Spring o del controlador REST, inyecta el contexto de OTel en un mapa y extráelo en OpenTracing:

    @PostMapping(value = "/submit")
    public CompletionStage<ResponseEntity<Void>> helloMapping(@RequestHeader Map<String, String> headers, @RequestBody String number) {
        //crear un mapa vacío para transportar el contexto
        var otelContext = new HashMap<String, String>();
        //poner el contexto de OTel en el mapa
        GlobalOpenTelemetry.get().getPropagators().getTextMapPropagator().inject(Context.current(), otelContext, (carrier, key, value) -> carrier.put(key, value));
        //poner el contenido del mapa en un OpenTracing SpanContext
        var openTracingContext = GlobalTracer.get().extract(Format.Builtin.TEXT_MAP, new TextMapAdapter(otelContext));
        //establecer el nuevo OpenTracing SpanContext como el actual
        GlobalExtendedTracer.get().local().activateContext(openTracingContext);
    
        //llamar a IPF
        return MyDomain.initiation().handle(new InitiateMyFlowInput.Builder().build())
            .thenCompose(__ -> ResponseEntity.accepted().build);
    }