OpenTelemetry: Der vollständige Guide zu Observability 2026
OpenTelemetry hat die Art, wie wir Anwendungen beobachten, grundlegend verändert. Wo früher jedes Monitoring-Tool seinen eigenen Agent, sein eigenes SDK und sein eigenes Datenformat hatte, bietet OpenTelemetry heute einen einheitlichen, vendor-agnostischen Standard. Dieser Guide erklärt die Kernkonzepte und den praktischen Einsatz.
Was ist OpenTelemetry?
OpenTelemetry (kurz: OTel) ist ein CNCF-Projekt (Cloud Native Computing Foundation), das aus der Fusion von OpenCensus und OpenTracing entstanden ist. Es definiert einen Standard für die Erfassung, Verarbeitung und den Export von Telemetrie-Daten.
Die drei Säulen der Observability:
- Traces — Verfolgung einzelner Requests durch ein verteiltes System (wer hat was wann aufgerufen?)
- Metrics — Numerische Messwerte über Zeit (Anfragen/Sekunde, Latenz, Fehlerrate)
- Logs — Strukturierte Ereignis-Aufzeichnungen mit Kontext
OpenTelemetry verbindet alle drei zu einem kohärenten Observability-Bild — und zwar framework- und vendor-agnostisch.
Das OpenTelemetry-Ökosystem
OpenTelemetry SDK
Für jede Programmiersprache gibt es ein offizielle OTel-SDK:
- JavaScript/TypeScript (
@opentelemetry/api,@opentelemetry/sdk-node) - Python (
opentelemetry-api,opentelemetry-sdk) - Go (
go.opentelemetry.io/otel) - Java (OTel Java Agent — Zero-Code-Instrumentation)
- Rust (
opentelemetrycrate)
OpenTelemetry Collector
Der OTel Collector ist das Herzstück vieler OTel-Deployments: Er empfängt Telemetrie-Daten von Anwendungen, verarbeitet sie (Filtering, Sampling, Enrichment) und exportiert sie zu verschiedenen Backends.
App (OTel SDK) → OTel Collector → [Jaeger, Prometheus, Grafana Loki, ...]
Der Collector entkoppelt Anwendung und Backend — das Backend kann gewechselt werden, ohne den Anwendungs-Code zu ändern.
OTLP: Das Protokoll
OTLP (OpenTelemetry Protocol) ist das Übertragungsprotokoll für OTel-Daten. Es basiert auf Protobuf und unterstützt gRPC und HTTP/JSON. Alle modernen Observability-Backends (Jaeger, Tempo, Prometheus, Loki, Datadog, New Relic) unterstützen heute OTLP-Ingestion.
Traces: Distributed Tracing verstehen
Ein Trace repräsentiert den Lebensweg eines einzelnen Requests durch ein System. Er besteht aus Spans — Einheiten von Arbeit mit Start- und Endzeit, Attributen und Statuscode.
Beispiel: Ein HTTP-Request an eine API:
[HTTP Request /api/users] (50ms)
├── [Auth Middleware] (2ms)
├── [DB Query: SELECT users] (40ms)
│ └── [TCP Connect to DB] (5ms)
└── [JSON Serialization] (1ms)
Jeder Span hat:
- Trace ID — eindeutige ID des gesamten Requests
- Span ID — eindeutige ID dieses spezifischen Spans
- Parent Span ID — ID des übergeordneten Spans (für Baumstruktur)
- Attributes — Key-Value-Metadaten (
http.method,db.statement, etc.) - Events — zeitgestempelte Ereignisse innerhalb des Spans
- Status — OK, ERROR, UNSET
Automatische Instrumentierung
Für viele Frameworks gibt es Zero-Code-Instrumentation:
Node.js:
// Automatische Instrumentierung für HTTP, Express, gRPC, etc.
const { NodeSDK } = require('@opentelemetry/sdk-node')
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
const sdk = new NodeSDK({
instrumentations: [getNodeAutoInstrumentations()],
})
sdk.start()
Das instrumentiert automatisch: HTTP-Module, Express/Fastify/Koa, gRPC, MySQL, PostgreSQL, Redis, MongoDB und Dutzende weitere Libraries — ohne den eigentlichen Anwendungs-Code zu ändern.
Java:
# Java Agent — keine Code-Änderungen nötig
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=my-service \
-Dotel.exporter.otlp.endpoint=http://collector:4317 \
-jar myapp.jar
Manuelle Instrumentierung
Für business-spezifische Traces schreibt man Custom Spans:
const { trace } = require('@opentelemetry/api')
const tracer = trace.getTracer('my-service', '1.0.0')
async function processOrder(orderId) {
return tracer.startActiveSpan('processOrder', async (span) => {
span.setAttribute('order.id', orderId)
try {
const result = await db.fetchOrder(orderId)
span.setAttribute('order.items', result.items.length)
return result
} catch (err) {
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message })
span.recordException(err)
throw err
} finally {
span.end()
}
})
}
Metrics: Von Prometheus-Format zu OTLP
OpenTelemetry unterstützt gängige Metrik-Typen:
- Counter — Monoton steigende Werte (Requests, Errors)
- Gauge — Aktueller Wert (RAM-Verbrauch, aktive Verbindungen)
- Histogram — Verteilung von Werten (Request-Latenz-Quantile)
const { metrics } = require('@opentelemetry/api')
const meter = metrics.getMeter('my-service')
const requestCounter = meter.createCounter('http_requests_total', {
description: 'Total HTTP requests',
})
const requestDuration = meter.createHistogram('http_request_duration_ms', {
description: 'HTTP request duration in milliseconds',
})
// Verwendung
app.use((req, res, next) => {
const start = Date.now()
res.on('finish', () => {
requestCounter.add(1, { method: req.method, status: res.statusCode })
requestDuration.record(Date.now() - start, { route: req.path })
})
next()
})
Der OTel Collector: Konfiguration
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# Prometheus Scraping auch möglich
prometheus:
config:
scrape_configs:
- job_name: 'my-service'
static_configs:
- targets: ['my-service:8080']
processors:
batch:
timeout: 1s
send_batch_size: 1024
# Sensible Daten entfernen
attributes:
actions:
- key: db.statement
action: delete
exporters:
# Zu Jaeger für Traces
jaeger:
endpoint: jaeger:14250
# Zu Prometheus für Metrics
prometheus:
endpoint: "0.0.0.0:8889"
# Zu Loki für Logs
loki:
endpoint: http://loki:3100/loki/api/v1/push
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, attributes]
exporters: [jaeger]
metrics:
receivers: [otlp, prometheus]
processors: [batch]
exporters: [prometheus]
logs:
receivers: [otlp]
processors: [batch]
exporters: [loki]
Exemplars: Traces und Metrics verbinden
Ein mächtiges OTel-Feature: Exemplars verbinden Metrics mit Traces. Wenn die Latenz plötzlich steigt, kann direkt auf einen repräsentativen Trace geklickt werden — ohne separat suchen zu müssen.
In Grafana: Prometheus-Metrik mit Exemplars → Direktlink zu Jaeger/Tempo-Trace. Das verkürzt die Mean Time to Resolution (MTTR) erheblich.
Sampling: Kosten kontrollieren
Tracing jeder Anfrage ist teuer. Sampling-Strategien:
Head-Based Sampling: Entscheidung beim ersten Span — z.B. nur 10% der Traces speichern.
Tail-Based Sampling (im Collector): Erst wenn der Trace vollständig ist, Entscheidung treffen — z.B. alle Error-Traces behalten, aber nur 1% der OK-Traces.
# Tail Sampling im OTel Collector
processors:
tail_sampling:
decision_wait: 10s
policies:
- name: errors-policy
type: status_code
status_code: {status_codes: [ERROR]}
- name: probabilistic-policy
type: probabilistic
probabilistic: {sampling_percentage: 10}
OTel im Self-Hosting-Stack
Für einen vollständigen Self-Hosted Observability Stack:
| Komponente | Tool | Rolle |
|---|---|---|
| Traces | Jaeger oder Tempo | Trace-Storage & UI |
| Metrics | Prometheus | Metric-Storage |
| Logs | Loki | Log-Aggregation |
| Visualisierung | Grafana | Unified Dashboard |
| Kollektor | OTel Collector | Daten-Pipeline |
Grafana bietet heute native Integration aller vier Systeme und ermöglicht es, von einer Metric-Anomalie direkt zum zugehörigen Trace zu navigieren — das ist die Stärke von OpenTelemetry als einheitlichem Standard.
Fazit: Warum OpenTelemetry?
OpenTelemetry hat das Observability-Landschaft grundlegend verändert:
- Vendor-Agnostisch: Code bleibt unverändert, egal ob Jaeger, Datadog oder New Relic als Backend gewählt wird
- Einheitliches Datenmodell: Traces, Metrics und Logs nach einem gemeinsamen Standard
- Community-getrieben: CNCF-Projekt mit Support von Google, Microsoft, AWS, Datadog und Hunderten weiterer Unternehmen
- Zukunftssicher: OTel ist der defacto-Standard für Cloud-Native Observability
Für jeden, der microservice-basierte Anwendungen betreibt, ist OpenTelemetry heute keine Option mehr — es ist die Grundlage jeder ernsthaften Observability-Strategie.