paint-brush
OpenTelemetry Tracing in Spring Boot: Auswahl zwischen Java Agent und Micrometer fürvon@nfrankel
193 Lesungen

OpenTelemetry Tracing in Spring Boot: Auswahl zwischen Java Agent und Micrometer für

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

Zu lang; Lesen

Dieser Beitrag vergleicht OpenTelemetry-Tracing-Lösungen in einer Spring Boot-Anwendung mit Java Agent v1, Java Agent v2 und Micrometer Tracing. Er hebt Unterschiede in Konfiguration, Funktionalität und Tracing-Fähigkeiten hervor und bietet Einblicke in die Wirksamkeit und Anwendung jedes Ansatzes zur Beobachtung.
featured image - OpenTelemetry Tracing in Spring Boot: Auswahl zwischen Java Agent und Micrometer für
Nicolas Fränkel HackerNoon profile picture

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.

Die Basisanwendung und ihre Infrastruktur

Ich verwende dieselbe Basisanwendung: eine einfache Spring Boot-Anwendung, die in Kotlin codiert ist. Sie bietet einen einzelnen Endpunkt.


  • Die Funktion hinter dem Endpunkt heißt entry()
  • Es ruft eine andere Funktion namens intermediate() auf.
  • Letzteres verwendet eine WebClient Instanz, den Ersatz für RestTemplate , um einen Aufruf des obigen Endpunkts zu tätigen
  • Um Endlosschleifen zu vermeiden, übergebe ich einen benutzerdefinierten Anforderungsheader: Wenn die Funktion entry() ihn findet, fährt sie nicht fort


Beispiel für ein App-Sequenzdiagramm


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.

Mikrometer-Abtastung

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.


-- Mikrometer-Tracing-Site


Um mit Micrometer Tracing zu beginnen, müssen einige Abhängigkeiten hinzugefügt werden:


  • Spring Boot-Aktuator, org.springframework.boot:spring-boot-starter-actuator
  • Mikrometer-Tracing selbst, io.micrometer:micrometer-tracing
  • Eine „Brücke“ zur Ziel-Tracing-Backend-API. In meinem Fall ist es OpenTelemetry, daher io.micrometer:micrometer-tracing-bridge-otel
  • Ein konkreter Exporter zum Backend, 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
  1. Aktivieren Sie den OpenTelemetry-Collector für Jaeger
  2. Vollständige URL zum Jaeger OpenTelemetry gRPC-Endpunkt
  3. Legen Sie den Dienstnamen von OpenTelemetry fest


Hier ist das Ergebnis:

Mikrometerspuren auf Jaeger ohne Anpassung


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.


Klassendiagramm der Observation-API

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:


Mikrometerspuren auf Jaeger mit der Observation API

OpenTelemetry Agent v1

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"
  1. Legen Sie das Protokoll, die Domäne und den Port fest. Die Bibliothek hängt /v1/traces an.
  2. Legen Sie den Dienstnamen von OpenTelemetry fest
  3. Exportieren Sie weder die Metriken noch die Protokolle


Ohne weitere Konfiguration erhalten wir die folgenden Spuren:


Agent v1 verfolgt Jaeger ohne Anpassung


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.

@WithSpan-Klassendiagramm


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

Agent v1-Traces auf Jaeger mit Anmerkungen

OpenTelemetry Agent v2

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.

Agent v2 verfolgt Jaeger ohne Anpassung


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.

Agent v2-Traces auf Jaeger mit Anmerkungen


Diskussion

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