LinkMesh

Doku, Blog und Changelog durchsuchen

Die Dokumentation ist nur auf Englisch verfügbar.

ENDE
Ein Hauptschalthebel, über eine einzige Koppelstange mit einer Bank identischer Ventilantriebe verbunden
LinkMeshObservability Data Collection Management
OpenTelemetryOperations

Java Agent in Produktion

Ein Flag instrumentiert alles. Das ist ebenso das Problem wie der Sinn.

linkmesh.io
6 Min. Lesezeit

Der OpenTelemetry Java Agent ist das wirksamste einzelne Artefakt des ganzen Projekts: ein -javaagent:-Flag, und ein Spring-Boot-Service, den Sie nie angefasst haben, emittiert Traces für jeden HTTP-Request, jeden JDBC-Aufruf, jede Kafka-Nachricht und jeden ausgehenden Client-Aufruf, dazu JVM-Metriken, mit W3C-Context-Propagation über alles hinweg. Keine Codeänderung. Deshalb ist er die Standardantwort, wann immer ein Java-Bestand einen Anbieter-APM-Agenten verlassen muss.

Und deshalb verbrennen sich Teams in der ersten Produktionswoche die Finger. Die Standardwerte des Agenten sind auf Abdeckung abgestimmt, nicht auf Ihren Traffic — und in dem Moment, in dem er auf eine echte Request-Rate trifft, entdecken Sie, welche der hundert Instrumentierungen Sie nicht wollten, was eine Sampling-Rate von 100 % kostet und dass Ihre Logs nun an zwei Orte gehen. Dieser Beitrag ist die Produktions-Checkliste: was der Agent tut, was er kostet und was Sie ändern, bevor er live geht.

Für wen dieser Leitfaden ist

Engineers, die den Java Agent auf Services mit echtem Traffic ausrollen — typischerweise im Rahmen einer Anbieter-APM-Migration. Voraussetzungen: Sie können JVM-Flags und Umgebungsvariablen Ihrer Services ändern und haben einen OTLP-Endpunkt als Ziel. Dies ist das anwendungsseitige Gegenstück zu den Collector-seitigen Beiträgen dieses Blogs; es endet an der Eingangstür des Collectors.

Was Sie mit einem Flag bekommen

java -javaagent:/opt/otel/opentelemetry-javaagent.jar \
     -Dotel.service.name=payments-api \
     -Dotel.exporter.otlp.endpoint=http://localhost:4317 \
     -jar payments-api.jar

Beim JVM-Start angehängt, schreibt der Agent Bytecode um, während Klassen geladen werden, und instrumentiert, welche seiner unterstützten Bibliotheken er findet — Servlet-Container, Spring, JDBC-Treiber, HTTP-Clients, Kafka, gRPC, Redis-Clients und einen langen Rattenschwanz. Daraus erhalten Sie:

  • Traces für jeden Server-Request und jeden ausgehenden Aufruf, mit Parent-Child-Spans und W3C-traceparent-Propagation, sodass ein Request über vier Services ein Trace ist.
  • JVM-Laufzeitmetriken — Heap- und Non-Heap-Belegung, GC-Pausen (Anzahl und Dauer), Thread-Zahlen, Klassenladen — unter den jvm.*-Semantic-Conventions.
  • Log-Korrelation: Trace- und Span-IDs werden in den MDC injiziert, sodass bestehende Logback- oder Log4j2-Ausgabe sie tragen kann; optional werden die Logs selbst über OTLP exportiert.

Alles im OTLP-Wire-Format, dorthin, wo Sie es hinrichten. Diese letzte Eigenschaft ist der Sinn der Übung — die ehrliche Abgrenzung der Migration, die er ermöglicht, steht in was OpenTelemetry ersetzen kann und was nicht.

Die Produktions-Checkliste

Sechs Dinge, die vor dem ersten echten Deployment zu klären sind, ungefähr in der Reihenfolge, in der sie beissen:

1. An einen lokalen Collector senden, nie direkt ans Backend. Der Exporter des Agenten ist in Ordnung, aber er läuft in Ihrem Prozess: Retries, Queueing und Backpressure passieren im Arbeitsspeicher Ihres Services und auf seinen Threads. Ein Collector auf localhost (oder dem Node) fängt das ab, batcht und gibt Ihnen einen Ausgangspunkt, durch den Sie maskieren, samplen und routen — das ganze Argument aus Was ist eine Telemetrie-Pipeline. Es heisst auch, dass ein Backend-Ausfall den Collector beeinträchtigt, nicht Ihren Service.

2. Sampling explizit setzen. Standardmässig wird alles gesampelt, worüber kein Parent entschieden hat — was bei 100 % am ersten Tag richtig und im Grossen falsch ist. Das produktionssichere Muster ist Parent-basiertes Ratio-Sampling am Agenten, wobei die Regel Fehler und langsame Traces behalten per Tail-Sampling am Gateway durchgesetzt wird — der Agent kann den Ausgang eines Trace nicht sehen, an dessen Wurzel er steht. Raten gehören unter zentrale Governance, nicht pro Service: Sampling-Governance.

-Dotel.traces.sampler=parentbased_traceidratio
-Dotel.traces.sampler.arg=0.1

3. Die Instrumentierungen abschalten, die Sie nicht wollen. Der Agent aktiviert alles, was er erkennt. Übliche Kandidaten zum Abschalten am ersten Tag: rein interne Bibliotheken, die pro Methode einen Span erzeugen, Health-Check-Endpunkte, die pro Probe einen Trace erzeugen, und jede Instrumentierung, deren Spans Sie sich nie angesehen haben. Jede ist eine Property:

-Dotel.instrumentation.<name>.enabled=false

Gehen Sie per Allow-List vor statt per Jagd: die üblichen Standards abschalten, nur aktivieren, was Sie nutzen, dann erweitern. Der abgeschaltete Satz ist Konfiguration, die mit dem Service versioniert gehört.

4. Entscheiden, wohin Logs gehen — einmal. Es gibt zwei Pfade, und beide zu betreiben ist der klassische Fehler: Der Agent kann Logs über die Logback-/Log4j2-Appender-Bridge per OTLP exportieren, und Ihre bestehenden Datei- oder stdout-Logs werden vermutlich bereits von einem Collector getailt. Wählen Sie einen. Tailt der Collector schon Dateien, behalten Sie das und lassen den Agenten nur Trace-IDs in den MDC injizieren; wollen Sie strukturierte Logs mit angehängten Resource-Attributen, nutzen Sie die Bridge und hören auf zu tailen. Beides ist doppelte Aufnahme mit doppelter Rechnung.

5. Die Startkosten budgetieren. Bytecode-Instrumentierung passiert beim Klassenladen, der Start ist also langsamer — bei einer grossen Spring-Anwendung häufig einige Sekunden, gelegentlich mehr. Auf Kubernetes wechselwirkt das mit Readiness-Probes und Rolling Deploys; bei Serverless oder Scale-to-Zero ist es ein echtes Problem. Messen Sie es auf Ihrem langsamsten Service, bevor es ein Deployment überrascht. Der Overhead im Dauerbetrieb liegt meist im niedrigen einstelligen CPU-Prozentbereich — aber erst, wenn Sampling und Instrumentierungsumfang gesetzt sind; mit Standardwerten kann er spürbar höher sein.

6. Bereinigen, was den Prozess verlässt. Die JDBC-Instrumentierung erfasst Statements; standardmässig bereinigt der Agent Literale, und das wollen Sie eingeschaltet lassen. Die HTTP-Instrumentierung kann auf Wunsch Header erfassen; wünschen Sie sich nicht Authorization. Alles, was der Agent auf ein Span-Attribut legt, ist auf dem Weg zu einem Backend, also gilt dieselbe Maskierungsdisziplin wie für Logs — PII maskieren — und der Collector ist der Ort, sie flottenweit durchzusetzen, denn Sie werden nicht die Flags jedes Services auditieren.

Dinge, die Sie überraschen werden

  • Versionsversatz über Services hinweg. Der Agent ist ein Jar, das Sie mit jedem Service ausliefern; hundert Services sind hundert Kopien in der Version, auf die jedes Team zuletzt angehoben hat. Änderungen der Semantic Conventions zwischen Agent-Versionen können Attribute umbenennen, die Ihre Dashboards nutzen. Pinnen Sie ihn und rollen Sie ihn bewusst aus — dieselbe Disziplin wie für die Collector-Flotte, eine Schicht tiefer.
  • Agent und SDK sind verschiedene Dinge. Teams, die später manuelle Spans über die API ergänzen, bekommen die Auto-Instrumentierung des Agenten und ihre eigenen Spans in einem Trace — das beabsichtigte Modell. Das SDK separat zusätzlich zum Agenten einzubinden ist es nicht und erzeugt doppelte Exporter.
  • Frameworks, die der Agent nicht kennt. Hauseigene RPC-Schichten, ungewöhnliche Thread-Pools und reaktiver Code, der Threads wechselt, können den Kontext verlieren. Die Abhilfe ist meist ein wenig manuelle Propagation, kein anderer Agent — aber sie sollte im Staging gefunden werden, nicht durch einen Vorfall mit abgeschnittenem Trace.

Die ehrliche Position

Der Java Agent ist das Beste, was OpenTelemetry ausliefert, und der Grund, warum die meisten Java-Bestände einen Anbieter-APM überhaupt verlassen können. Er ist auch eine Komponente mit produktionsreifem Wirkungsradius, die mit Tutorial-Standardwerten ausgerollt wird. Behandeln Sie die sechs Punkte oben als Go-live-Schranke, stellen Sie den Agenten hinter einen Collector, damit die flottenweiten Entscheidungen an einer Stelle fallen, und versionieren Sie den Agenten wie die Abhängigkeit, die er ist.

Agents auf jeder JVM und ein Collector auf jedem Host?

LinkMesh verwaltet die OpenTelemetry Collectors, an die Ihre Java Agents exportieren, von einer selbst gehosteten Control Plane aus — Tail-Sampling, Maskierung und Routing einmal zusammengestellt und auf jeden Node ausgerollt, sodass die Agent-Flags pro Service einfach bleiben und die flottenweiten Entscheidungen dort fallen, wo Sie sie sehen. Sehen Sie, was es kann, oder stellen Sie eine auf in wenigen Minuten.