Meine Demo von OpenTelemetry Tracing umfasst zwei Spring Boot-Komponenten. Eine verwendet den Java-Agenten und mir ist ein anderes Verhalten aufgefallen, als ich ihn kürzlich von v1.x auf v2.x aktualisiert habe. In der anderen verwende ich Micrometer Tracing, weil ich in GraalVM nativ kompiliere und es keine Java-Agenten verarbeiten kann.
In diesem Beitrag möchte ich diese drei Ansätze vergleichen: Java-Agent v1, Java-Agent v2 und Micrometer Tracing.
Ich verwende dieselbe Basisanwendung: eine einfache Spring Boot-Anwendung, die in Kotlin codiert ist. Sie bietet einen einzelnen Endpunkt.
entry()
intermediate()
auf.WebClient
Instanz, den Ersatz für RestTemplate
, um einen Aufruf des obigen Endpunkts zu tätigenentry()
ihn findet, fährt sie nicht fort
Es wird in den folgenden Code übersetzt:
@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() } }
Bei jedem Setup überprüfe ich zwei Phasen: die primäre Phase mit aktivierter OpenTelemetry und eine Anpassungsphase zum Erstellen zusätzlicher interner Bereiche.
Micrometer Tracing stammt von Micrometer , einer „herstellerneutralen Fassade zur Anwendungsbeobachtung“.
Micrometer Tracing bietet eine einfache Fassade für die gängigsten Tracer-Bibliotheken, sodass Sie Ihren JVM-basierten Anwendungscode ohne Abhängigkeit von einem Anbieter instrumentieren können. Es ist so konzipiert, dass es Ihrer Tracing-Sammelaktivität wenig bis gar keinen Mehraufwand hinzufügt und gleichzeitig die Portabilität Ihrer Tracing-Bemühungen maximiert.
Um mit Micrometer Tracing zu beginnen, müssen einige Abhängigkeiten hinzugefügt werden:
org.springframework.boot:spring-boot-starter-actuator
io.micrometer:micrometer-tracing
io.micrometer:micrometer-tracing-bridge-otel
io.opentelemetry:opentelemetry-exporter-otlp
Wir benötigen keine Stückliste, da im übergeordneten Spring Boot-Element bereits Versionen definiert sind.
Wir benötigen jedoch zwei Laufzeitkonfigurationsparameter: Wohin sollen die Traces gesendet werden und wie lautet der Name der Komponente? Sie werden durch die Variablen MANAGEMENT_OTLP_TRACING_ENDPOINT
und SPRING_APPLICATION_NAME
gesteuert.
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
Hier ist das Ergebnis:
Ohne jegliche Anpassung erstellt Micrometer Spans beim Empfangen und Senden von HTTP-Anfragen.
Das Framework muss Magie in den RestClient
einbringen, um ihn zu senden. Dazu müssen wir den RestClient vom RestClient instanziieren lassen:
@SpringBootApplication class MicrometerTracingApplication { @Bean fun restClient(builder: RestClient.Builder) = builder.baseUrl("http://localhost:8080/done").build() }
Wir können manuelle Spannen auf verschiedene Weise erstellen, eine davon über die OpenTelemetry-API selbst. Die Einrichtung erfordert jedoch viel Boilerplate-Code. Der einfachste Weg ist die Observation-API von Micrometer. Ihr Hauptvorteil besteht darin, dass sie eine einzige API verwendet, die sowohl Metriken als auch Traces verwaltet.
Hier ist der aktualisierte Code:
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() } }
Die hinzugefügten Beobachtungsaufrufe spiegeln die generierten Spuren wider:
Eine Alternative zu Micrometer Tracing ist der generische OpenTelemetry Java Agent . Sein Hauptvorteil besteht darin, dass er weder den Code noch die Entwickler beeinflusst. Der Agent ist ein reines Laufzeitproblem.
java -javaagent:opentelemetry-javaagent.jar agent-one-1.0-SNAPSHOT.jar
Der Agent hält sich an die Konfiguration von OpenTelemetry mit Umgebungsvariablen:
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"
/v1/traces
an.
Ohne weitere Konfiguration erhalten wir die folgenden Spuren:
Der Agent verfolgt automatisch empfangene und gesendete Anfragen sowie Funktionen, die mit Spring-bezogenen Annotationen markiert sind . Traces sind entsprechend dem Aufrufstapel korrekt ineinander verschachtelt. Um zusätzliche Funktionen zu verfolgen, müssen wir unserer Codebasis eine Abhängigkeit hinzufügen: io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations
. Wir können jetzt zuvor nicht verfolgte Funktionen mit der Annotation @WithSpan
annotieren.
Der value()
Teil bestimmt die Bezeichnung der Ablaufverfolgung, während kind
als span.kind
-Attribut übersetzt wird. Wenn der Wert auf eine leere Zeichenfolge gesetzt ist, was der Standard ist, wird der Name der Funktion ausgegeben. Für meine Zwecke sind Standardwerte gut genug.
@WithSpan fun intermediate() { logger.info("intermediate") RestClient.builder() .baseUrl("http://localhost:8080/done") .build() .get() .header("X-done", "true") .retrieve() .toBodilessEntity() }
Es ergibt sich die erwartete neue intermediate()
:
OpenTelemetry hat im Januar dieses Jahres eine neue Hauptversion des Agenten veröffentlicht. Ich habe meine Demo damit aktualisiert; Traces werden jetzt nur noch erstellt, wenn die App Anfragen empfängt und sendet.
Wie bei der vorherigen Version können wir Spuren mit der Annotation @WithSpan
hinzufügen. Der einzige Unterschied besteht darin, dass wir auch die Funktion entry()
annotieren müssen. Sie wird standardmäßig nicht verfolgt.
Spring war aus zwei Gründen erfolgreich: Es vereinfachte komplexe Lösungen, z . B. EJBs 2, und bot eine Abstraktionsschicht gegenüber konkurrierenden Bibliotheken. Micrometer Tracing begann als Abstraktionsschicht über Zipkin und Jaeger und machte absolut Sinn. Dieses Argument ist hinfällig, da OpenTelemetry von den meisten Bibliotheken in Programmiersprachen und Trace-Sammlern unterstützt wird. Die Observation API ist immer noch ein erheblicher Vorteil von Micrometer Tracing, da es eine einzige API über Metrics und Traces verwendet.
Auf der Java-Agent-Seite ist die OpenTelemetry-Konfiguration bei allen Tech-Stacks und Bibliotheken ähnlich – Umgebungsvariablen. Ich war etwas enttäuscht, als ich von v1 auf v2 aktualisierte, da der neue Agent nicht Spring-fähig ist: Mit Spring annotierte Funktionen werden standardmäßig nicht verfolgt.
Letztendlich ist es eine kluge Entscheidung. Es ist viel besser, die gewünschten Spannen explizit anzugeben, als einige zu entfernen, die Sie nicht sehen möchten.
Danke an Jonatan Ivanov für seine Hilfe und seine Rezension .
Der vollständige Quellcode für diesen Beitrag ist auf GitHub zu finden:
Um weiter zu gehen:
Ursprünglich veröffentlicht bei A Java Geek am 3. August 2024