Start / Cheat Sheets

Cheat Sheet

OpenTelemetry-Instrumentierung — Cheat Sheet

Stand: · Observability mit OpenTelemetry, Prometheus und Grafana

OpenTelemetryTracingCollectorSampling

Der erste von zwei Spickzetteln zum Seminar Observability mit OpenTelemetry, Prometheus und Grafana: alles zwischen Anwendung und Backend. Was danach kommt — PromQL, Dashboards, Alarme, SLOs — steht auf Prometheus und Grafana.

Maßgeblich ist die Spezifikation auf opentelemetry.io. Dieses Blatt trifft eine Auswahl: die Entscheidungen, die man beim Instrumentieren tatsächlich trifft, und die Fallen, die still bleiben statt Fehler zu werfen.

Welches Signal beantwortet welche Frage

SignalStark beiSchwach bei
MetrikTrend, Schwellwert, AlarmEinzelfall und Ursache
TraceUrsachensuche über DienstgrenzenAussage über die Gesamtheit
LogDetail zu einem EreignisAggregation und Vergleich

Die Schwäche jedes Signals ist die Stärke eines anderen — deshalb lohnt die Korrelation. Als Faustregel: Fragen mit „welche” und „wie viele” führen auf Metriken, Fragen mit „warum” auf Traces, Fragen nach dem Wortlaut auf Logs.

FehlerbildWas hilft
bekannt und erwartetMonitoring mit Schwellwert
bekannt, Ursache offenTrace über alle Dienste
unbekanntfreie Abfrage über Attribute

Die dritte Zeile ist der Grund für Observability — sie lässt sich nicht vorab als Alarm formulieren.

Module: Von Monitoring zu Observability · Die Observability-Signale

OTLP: Ports, Protokolle, Pfade

TransportPortMerkmale
OTLP/gRPC4317unärer Austausch, Statuscodes des gRPC-Protokolls
OTLP/HTTP binär4318Protobuf, Content-Type application/x-protobuf
OTLP/HTTP JSON4318Trace- und Span-IDs als Hex-Zeichenkette

Die Pfade lauten /v1/traces, /v1/metrics und /v1/logs; Profiles liegen unter /v1development/profiles. Häufigster Fehlgriff: gRPC-Port mit HTTP-Protokoll kombiniert — der Export läuft dann still ins Leere. Ebenso häufig: den Endpunkt mit Pfad angeben, obwohl das SDK ihn selbst anhängt.

BetriebsformStärkeGrenze
AgentHost-Attribute, kurzer Weg, kein Verlust im Netzviele Instanzen zu pflegen
Sidecarklare Zuordnung je DienstRessourcen je Pod
GatewaySampling und Routing über alle DiensteAusfallpunkt ohne Redundanz

Agent und Gateway schließen sich nicht aus — die Kette Agent → Gateway ist der Regelfall. Entscheidungen über alle Dienste hinweg, etwa Tail Sampling, brauchen zwingend ein Gateway.

Module: Referenzarchitektur · OpenTelemetry Collector

Resource-Attribute und Semantic Conventions

AttributStabilitätBeispiel
service.namestabil, vom SDK verlangtpfandwerk-annahme
service.versionstabil2.4.0
service.instance.idstabilannahme-7c4f
deployment.environment.namestabilproduction
telemetry.sdk.namestabil, vom SDK gesetztopentelemetry

Das ältere deployment.environment ist abgekündigt und durch deployment.environment.name ersetzt. Ohne Codeänderung gesetzt:

OTEL_SERVICE_NAME=pfandwerk-annahme
OTEL_RESOURCE_ATTRIBUTES=\
service.version=2.4.0,\
service.instance.id=annahme-7c4f,\
deployment.environment.name=production

Eigene Attribute benennen:

FallSchreibweiseBeispiel
firmenweitDomain rückwärtsde.pfandwerk.filiale
nur eine AnwendungAnwendungsname voranpfandwerk.materialart
zu vermeidenfremder Namensraumhttp.filiale

Bestehende OpenTelemetry-Namensräume nie als Präfix verwenden — künftige Versionen kollidieren sonst damit. Vor jedem eigenen Attribut prüfen, ob die Konventionen es bereits kennen; das ist der häufigste vermeidbare Fehler.

Span Kind steuert, wie Backends Dienstgrenzen erkennen — SERVER, CLIENT, PRODUCER, CONSUMER, INTERNAL. Es ist keine Schmuckangabe: Ist alles INTERNAL, sieht das Backend keine Dienstgrenze.

Modul: Datenmodell und Semantic Conventions

Java Agent: Konfiguration und Reichweite

java -javaagent:/opt/otel/opentelemetry-javaagent.jar \
  -Dotel.service.name=pfandwerk-annahme \
  -Dotel.exporter.otlp.protocol=grpc \
  -Dotel.exporter.otlp.endpoint=http://collector:4317 \
  -jar pfandwerk-annahme.jar
UmgebungsvariableSetztÜbliche Wahl
OTEL_SERVICE_NAMEservice.nameName des Dienstes
OTEL_EXPORTER_OTLP_ENDPOINTZiel des ExportsAdresse des Collectors
OTEL_EXPORTER_OTLP_PROTOCOLTransportformgrpc oder http/protobuf
OTEL_TRACES_EXPORTERTrace-Exportotlp
OTEL_METRICS_EXPORTERMetrik-Exportotlp
OTEL_LOGS_EXPORTERLog-Exportotlp

Jede Systemeigenschaft hat eine Entsprechung als Umgebungsvariable. In Containern lässt sich der Agent auch über JAVA_TOOL_OPTIONS anhängen, ohne den Startbefehl zu ändern. Eine einzelne Instrumentierung abschalten — besser als pauschal filtern, weil die Daten gar nicht erst entstehen:

-Dotel.instrumentation.jdbc.enabled=false

Was der Agent von allein erfasst: HTTP-Server und -Client (Latenz und Status je Endpunkt, Latenz externer Abhängigkeiten), JDBC (langsame Abfragen), JVM (Heap, Threads, Garbage Collection).

AutomatischManuell
technische Grenzen und Aufrufefachliche Vorgänge
ohne Codeänderungim Anwendungscode
konventionsgerechte Nameneigene Namensräume nötig
sofort verfügbarAufwand je Vorgang

Das Eine ersetzt das Andere nicht: Der Agent sieht, dass ein Endpunkt langsam war, nicht welcher Schritt darin. Fachliche Fehler ohne technischen Fehlerstatus bleiben unsichtbar.

Modul: Automatische Java-Instrumentierung

Eigene Spans und Metriken

Span span = tracer.spanBuilder("leergut.pruefen")
    .setSpanKind(SpanKind.INTERNAL).startSpan();
try (Scope scope = span.makeCurrent()) {
  span.setAttribute("pfandwerk.materialart", art);
  pruefe(behaelter);
} catch (Exception e) {
  span.recordException(e);
  span.setStatus(StatusCode.ERROR, "abgelehnt");
} finally { span.end(); }

Das finally ist nicht optional — ein nicht beendeter Span erscheint in keinem Backend. Attribute nach span.end() zu setzen wirkt nirgends.

LongCounter behaelter = meter
    .counterBuilder("pfandwerk.behaelter.angenommen")
    .setDescription("Angenommene Behaelter")
    .setUnit("{behaelter}")
    .build();
behaelter.add(1, Attributes.of(
    stringKey("pfandwerk.materialart"), "glas"));
InstrumentErfasstBeispiel
Countermonoton steigende Summeangenommene Behälter
HistogramVerteilung von MesswertenDauer der Materialprüfung
beobachtetMomentanwert bei AbfrageFüllstand des Automaten

Ein Wert, der auch fallen kann, gehört nie in einen Counter — sonst rechnet jede Rate falsch. Einheiten ohne physikalisches Maß stehen in geschweiften Klammern, Zeiten in s oder ms. Instrumente einmal beim Start bauen, nicht in der Schleife.

Modul: Manuelle Instrumentierung

Context Propagation: der traceparent und seine Brüche

traceparent: 00-a0892f3577b34da6a3ce929d0e0e4736
             -f03067aa0ba902b7-01

Aufbau: Version – Trace-ID – Span-ID – Flags, zusammen 55 Zeichen. Neben traceparent steht tracestate für herstellerspezifische Zusatzangaben.

BruchstelleUrsacheBehandlung
Thread-WechselContext ist threadgebundenContext ausdrücklich übergeben
WarteschlangeAnfrage endet vor VerarbeitungContext in die Nachricht legen
Stapelverarbeitungviele Ursachen, ein LaufSpan Link je Ursache
Wiederholungneuer Versuch, alter VorgangLink auf den ersten Versuch

Span Links verbinden, ohne eine Eltern-Kind-Beziehung zu behaupten, die es nicht gibt. Ein Bruch erzeugt keinen Fehler, nur einen zweiten, unauffälligen Trace — deshalb gehört die Prüfung in die Abnahme, nicht in die Störungssuche.

Baggage transportiert Werte über Dienstgrenzen. Es ist kein bequemer Transportweg für Anwendungsdaten: Was im Baggage steht, landet potenziell im Log eines Fremdsystems. Und Baggage, das nie als Attribut übernommen wird, bleibt wirkungslos.

Modul: Context Propagation und Baggage

Collector: Abschnitte, Pipeline, Processor-Reihenfolge

AbschnittAufgabeBeispiel
receiversTelemetrie entgegennehmenotlp
processorsverändern, begrenzen, anreichernbatch
exportersan ein Ziel weitergebenotlphttp
connectorsPipelines verbindenspanmetrics
extensionsZusatzfunktionen ohne Datenbezughealth_check
serviceaktivieren und verdrahtenpipelines

Nur was in service steht, ist aktiv. Eine definierte, aber nicht verdrahtete Komponente tut nichts — und meldet das auch nicht.

receivers:
  otlp: { protocols: { grpc: {} } }
processors:
  batch: { timeout: 10s }
exporters:
  debug: {}
service:
  pipelines:
    traces: { receivers: [otlp],
      processors: [batch], exporters: [debug] }
ProcessorAufgabeBeispiel
resourcedetectionHost- und PlattformangabenKnoten und Region ergänzen
attributeseinzelne Attribute ändernFiliale vereinheitlichen
transformRegeln mit Bedingung (OTTL)Status je Ablehnungsgrund
filterverwerfenHealth-Aufrufe entfernen
redactionWerte unkenntlich machenKontonummer entfernen
processors:
  attributes/filiale:
    actions:
      - key: pfandwerk.filiale
        value: unbekannt
        action: insert

insert setzt nur, wenn das Attribut fehlt; update ändert nur Vorhandenes, upsert tut beides. Eine OTTL-Anweisung mit Bedingung:

set(span.status.code, STATUS_CODE_OK)
  where span.attributes["pfandwerk.ablehnung"]
        == "material"

Die Reihenfolge der Processors ist die Reihenfolge der Wirkung: Memory-Limiter an den Anfang, sonst schützt er nichts · gefiltert wird vor dem Anreichern, sonst wird Verworfenes zuvor veredelt · Batch-Processor ans Ende, direkt vor den Export. Eine vertauschte Reihenfolge erzeugt keine Fehlermeldung, nur andere Daten.

Module: OpenTelemetry Collector · Telemetriedaten verarbeiten und kontrollieren

Sampling und Volumen

OTEL_TRACES_SAMPLERVerhaltenEinsatz
always_onerfasst allesEntwicklung
always_offerfasst nichtsgezielt abschalten
traceidratioAnteil nach Trace-IDselten allein sinnvoll
parentbased_traceidratiofolgt der Entscheidung des AufrufersRegelfall

OTEL_TRACES_SAMPLER_ARG trägt bei den Ratio-Samplern die Wahrscheinlichkeit zwischen 0 und 1. Die Entscheidung muss über alle Dienste hinweg dieselbe sein — entscheidet jeder Dienst neu, entstehen Traces mit Löchern. Ein abweichend konfigurierter Dienst genügt.

StellschraubeWirkungRisiko
Samplingweniger Tracesseltene Fälle verschwinden
Filterungweniger Rauschenspäter fehlt der Beleg
AggregationMetrik statt EinzelfallUrsachensuche wird schwerer
Aufbewahrungweniger Speicherkein Vorjahresvergleich

Fehler- und Latenzausreißer gehören in jeder Variante vollständig erhalten — sie tragen den Diagnosewert. Tail Sampling entscheidet erst, wenn der Trace vollständig ist, und braucht deshalb ein Gateway: Landen die Spans eines Trace auf verschiedenen Instanzen, greift die Regel ins Leere.

Modul: Sampling, Volumen und Kosten

Typische Fallen

  • Der Endpunkt passt nicht zum Protokoll. gRPC auf 4317, HTTP auf 4318 — die Verwechslung erzeugt keinen Fehler, nur fehlende Daten.
  • Alle Dienste heißen gleich. Der Agent läuft, aber OTEL_SERVICE_NAME fehlt.
  • Der Span-Name trägt eine Kennung. Bon-Nummer im Namen ergibt Millionen verschiedener Namen; Kennungen gehören in Attribute.
  • Alles ist INTERNAL. Ohne Span Kind erkennt kein Backend eine Dienstgrenze.
  • Der Span wird bei einer Ausnahme nie beendet. Ohne finally erscheint er in keinem Backend.
  • Ein Fehler wird geloggt, der Span-Status bleibt Unset. Umgekehrt: eine fachliche Ablehnung als ERROR treibt jede Fehlerquote grundlos hoch.
  • JDBC-Spans tragen die Abfrage mit eingesetzten Werten — und damit Personenbezug ins Backend.
  • Health-Endpunkte erzeugen die Mehrzahl aller Spans und verdecken den Rest.
  • Der asynchrone Teil erzeugt eigene Traces, und niemand bemerkt es monatelang — es entsteht kein Fehler, nur ein zweiter Trace.
  • Der Receiver steht in keiner Pipeline und empfängt nichts, obwohl er sauber konfiguriert ist.
  • Ohne Memory-Limiter reißt der Collector unter Last die ganze Instanz mit.
  • Ohne Batch-Processor erzeugt jeder einzelne Span einen Netzaufruf.
  • Die Redaction sitzt erst im Gateway, und der Agent hat die Daten schon geloggt.
  • Die Warteschlange liegt im Speicher, und ein Neustart verwirft alles.
  • Sampling wirkt doppelt — in der Anwendung und im Collector.
  • Metriken werden mitgesampelt und liefern danach falsche Absolutwerte.
  • Das Volumen wird nie gerechnet, und die erste Rechnung entscheidet die Architektur.

Zum Seminar Observability mit OpenTelemetry, Prometheus und Grafana