Prometheus + Grafana + OpenTelemetry + Sentry: Die Integrations-Checkliste für einen produktionsreifen Observability-Stack

#Prometheus Grafana OpenTelemetry Sentry Integration
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Die meisten Observability-Probleme sind keine Tool-Probleme. Es sind Integrationsprobleme. Prometheus scrapt Metriken, OpenTelemetry sendet Traces, Loki sammelt Logs und Sentry fängt Exceptions - aber jedes System lebt in seinem eigenen Silo. Um 2 Uhr nachts feuert ein Alert, und du verbringst zwanzig Minuten damit, Trace-IDs zwischen vier Browser-Tabs hin und her zu kopieren, um ein Bild zusammenzusetzen, das dein Tooling längst für dich hätte bauen sollen.

Dieser Beitrag liefert dir die Checkliste zur Prometheus Grafana OpenTelemetry Sentry Integration, die diese vier Systeme tatsächlich verbindet. Sie umfasst Collector-Konfiguration, SDK-Instrumentierung für Symfony und Next.js, die Verdrahtung der Grafana-Datasources, Alert-Routing vom Alertmanager zu Slack und PagerDuty sowie die drei Integrationsfehler, die Phantom-Alerts erzeugen - oder schlimmer: stille Ausfälle.


Der Vier-Schichten-Stack und wie die Teile zusammenpassen

Bevor wir in die Checkliste einsteigen, eine kurze Karte der Zuständigkeiten:

  • Prometheus sammelt und speichert Zeitreihen-Metriken. Es scrapt /metrics-Endpoints nach Zeitplan und wertet Alerting-Regeln aus.
  • Grafana visualisiert alles. Es dient als einheitliches Frontend für Prometheus, Loki, Tempo und - über das Sentry-Datasource-Plugin - deine Fehlervolumina.
  • OpenTelemetry (OTel) ist die Instrumentierungsschicht. Das OTel-SDK in deiner Anwendung sendet Traces, Metriken und Logs. Der OTel Collector empfängt sie und leitet sie an die passenden Backends weiter (Tempo für Traces, Loki für Logs, Prometheus Remote-Write für Metriken).
  • Sentry fängt Fehler, gruppiert sie zu Issues und trackt Releases. Seine größte Stärke sind Stacktraces mit Quellkontext und Regressionserkennung auf Release-Ebene.

Das Integrationsziel ist Korrelation: Wenn ein Grafana-Alert auf einer Prometheus-Metrik feuert, solltest du zu einem Tempo-Trace aus demselben Zeitfenster durchklicken können, die zugehörigen Loki-Logzeilen sehen und beim Sentry-Issue mit dem Stacktrace landen. Jeder Abschnitt unten bringt dich diesem Ziel einen Schritt näher.


Phase 1: Prometheus und der OTel Collector

Collector-Konfiguration

Der OTel Collector ist die zentrale Routing-Drehscheibe. Betreibe ihn als Sidecar oder als eigenen Service; Letzteres skaliert besser.

Checkliste:

  • Deploye otelcol-contrib (nicht den Core-Build - du brauchst den Prometheus-Exporter und den Loki-Exporter)
  • Konfiguriere einen prometheus-Receiver auf Port 9464, der den /metrics-Endpoint deiner Anwendung scrapt
  • Konfiguriere einen otlp-Receiver (gRPC auf 4317, HTTP auf 4318) für Traces und Logs aus dem SDK
  • Ergänze einen batch-Processor mit send_batch_size: 1024 und timeout: 5s, um die Backends nicht mit winzigen Payloads zu fluten
  • Ergänze einen memory_limiter-Processor mit 80% des Container-Memory-Limits - ohne ihn läuft der Collector in Produktion in ein OOM
  • Route Trace-Daten an einen otlp-Exporter Richtung Tempo
  • Route Log-Daten an einen loki-Exporter
  • Route Metriken an einen prometheusremotewrite-Exporter Richtung deiner Prometheus-Instanz (oder Mimir / Thanos bei horizontaler Skalierung)

Der häufigste Fehler hier: prometheusremotewrite nutzen und gleichzeitig einen separaten Prometheus-Scrape-Job für denselben Service laufen lassen. Das erzeugt doppelte Zeitreihen und erratische Rate-Berechnungen. Entscheide dich für einen Ingestion-Pfad.

Prometheus-Scrape-Konfiguration

Eine minimale Scrape-Konfiguration, die Label-Kollisionen vermeidet:

scrape_configs:
  - job_name: 'otel-collector'
    static_configs:
      - targets: ['otel-collector:8888']
  - job_name: 'your-app'
    static_configs:
      - targets: ['app:9464']
    honor_labels: true
    scrape_interval: 15s

Prüfe, dass die Prometheus-Seite /targets alle Jobs als UP zeigt, bevor du zu den Alerting-Regeln weitergehst.


Phase 2: SDK-Instrumentierung in Symfony und Next.js

Symfony (PHP)

Nutze open-telemetry/opentelemetry-auto-symfony für die automatische Instrumentierung von HTTP-Requests, Doctrine-Queries und Messenger-Dispatches.

composer require open-telemetry/sdk open-telemetry/opentelemetry-auto-symfony

Die wichtigsten Umgebungsvariablen für .env.prod:

OTEL_SERVICE_NAME=your-app-name
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_TRACES_SAMPLER=parentbased_traceidratio
OTEL_TRACES_SAMPLER_ARG=0.1

Checkliste:

  • Prüfe, dass ext-opentelemetry in der php.ini geladen ist - das Auto-Instrumentierungspaket benötigt die C-Extension
  • Ergänze eigene Spans für geschäftskritische Codepfade, die nicht automatisch instrumentiert werden (z. B. Preisberechnungen, Aufrufe von Dritt-APIs): nutze $tracer->spanBuilder('pricing.calculate')->startSpan()
  • Bestätige, dass Traces innerhalb von 30 Sekunden nach einem Test-Request in Tempo erscheinen

Next.js (Node)

npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node

Checkliste:

  • Lege instrumentation.ts im Projekt-Root an (Next.js 14+ registriert die Datei automatisch)
  • Initialisiere das NodeSDK mit einem OTLP-Exporter, der auf den Collector zeigt
  • Setze OTEL_SERVICE_NAME und OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production in deiner Umgebungskonfiguration
  • Nutze registerInstrumentations mit getNodeAutoInstrumentations() - das deckt HTTP, fetch, Datenbank-Clients und Redis automatisch ab
  • Verifiziere die Spans in Tempo. Wenn du Spans ohne Parent siehst (kaputter Trace-Baum), prüfe, ob traceparent-Header zwischen den Services propagiert werden - der W3C-Trace-Context-Propagator muss auf beiden Seiten konfiguriert sein

Phase 3: Grafana-Datasources verdrahten

Eine Grafana-Instanz, die nur Prometheus-Daten zeigt, ist halb verbunden. Das vollständige Setup braucht vier Datasources und zwei Exemplar-Links.

Checkliste:

  • Prometheus-Datasource hinzufügen - als Default markieren
  • Tempo-Datasource hinzufügen, Trace to logs auf Loki zeigen lassen mit traceId als Log-Label-Filter
  • Loki-Datasource hinzufügen, über Derived fields die traceId aus Logzeilen extrahieren und nach Tempo verlinken
  • In der Prometheus-Datasource Exemplars aktivieren und das URL-Template auf Tempo setzen - so kannst du auf eine Spitze im Latenz-Histogramm klicken und direkt zu einem Beispiel-Trace springen
  • Das Sentry-Datasource-Plugin (grafana-sentry-datasource) installieren und mit einem Sentry-Auth-Token verbinden, das auf project:read beschränkt ist
  • Prüfe im Explore-Tab, dass jede Datasource Daten liefert, bevor du Dashboards baust

Die zehn Panels, die jeder Service zum Launch braucht

Füge diese in ein einzelnes Dashboard pro Service ein. Sie decken die Signale ab, die zählen, bevor du dir vollumfängliches Observability-Engineering leisten kannst:

  1. Request-Rate - rate(http_server_requests_total[5m])
  2. Fehlerrate (%) - Fehler geteilt durch Gesamtanzahl der Requests
  3. p50 / p95 / p99 Latenz - aus dem Histogramm http_request_duration_seconds_bucket
  4. Aktive DB-Verbindungen
  5. Queue-Tiefe - falls Symfony Messenger oder eine Background-Job-Queue im Einsatz ist
  6. Speicherverbrauch vs. Limit
  7. CPU-Throttling - signalisiert Ressourcendruck im Container, bevor er zum Latenzproblem wird
  8. Sentry-Fehlervolumen - Issues pro Minute aus der Sentry-Datasource
  9. Deployment-Marker - annotiert aus der CI/CD über die Grafana-Annotations-API, damit du auf jedem Graphen exakt siehst, wann ein neues Release gelandet ist
  10. Apdex-Score - eine einzelne, menschenlesbare Zahl, die Latenz und Fehlerrate kombiniert

Phase 4: Alertmanager und Sentry Release-Tracking

Alertmanager zu Slack und PagerDuty

Prometheus wertet Alerting-Regeln aus; der Alertmanager übernimmt Routing, Deduplizierung und Silencing.

Checkliste:

  • Definiere Alerting-Regeln in einem rules/-Verzeichnis, das in Prometheus gemountet wird, nicht inline in der prometheus.yml - so lassen sie sich leichter in Git nachverfolgen
  • Starte mit vier Regeln: HighErrorRate (>1% für 5 Min.), HighLatency (p95 >1 s für 5 Min.), ServiceDown (up == 0 für 1 Min.), HighMemoryUsage (>90% für 10 Min.)
  • Konfiguriere Alertmanager-routes, die Alerts mit severity: critical an PagerDuty und severity: warning an einen Slack-Channel senden
  • Setze group_wait: 30s, group_interval: 5m, repeat_interval: 4h - die Defaults sind zu aggressiv und erzeugen innerhalb einer Woche Alert-Müdigkeit
  • Ergänze Inhibition-Regeln: Unterdrücke HighErrorRate und HighLatency, wenn für denselben Service bereits ServiceDown feuert - du brauchst keine drei gleichzeitigen Pages für einen Ausfall

Sentry Release-Tracking

Sentrys Release-Tracking wird zu selten genutzt, ist aber essenziell, um "dieser Bug existiert seit Wochen" von "diese Regression kam mit dem heutigen Deploy" zu unterscheiden.

Checkliste:

  • Setze SENTRY_RELEASE in deiner CI/CD-Pipeline auf den Git-Commit-SHA (oder nutze sentry-cli releases propose-version)
  • Rufe am Ende eines erfolgreichen Deploys sentry-cli releases finalize $RELEASE auf
  • Lade Source Maps aus dem Next.js-Build zu Sentry hoch, damit Stacktraces originales TypeScript statt minifiziertem Output zeigen
  • Konfiguriere traces_sample_rate im Sentry-SDK passend zu deiner OTel-Sampling-Rate - wenn OTel 10% sampelt und Sentry 100%, siehst du Performance-Transaktionen ohne korrespondierenden OTel-Trace
  • Aktiviere in den Sentry-Projekteinstellungen Connect traces und hinterlege das Tempo-URL-Muster, um direkte Links zwischen einem Sentry-Trace und dem zugehörigen Tempo-Trace zu erzeugen

Die drei Integrationsfehler, die Phantom-Alerts erzeugen

Nach Audits von Observability-Stacks bei SaaS-Kunden in verschiedenen Wachstumsphasen tauchen drei Konfigurationsfehler immer wieder auf.

1. Unterschiedliche Sampling-Raten zwischen den Tools. OTel mit 10% Sampling und Sentry mit 100% ist der häufigste Übeltäter. Sentry fängt einen Fehler, erzeugt einen sentry-trace-Header und versucht, ihn mit einem OTel-Trace zu korrelieren, der nie gesampelt wurde. Das Ergebnis: kaputte Trace-Links und der falsche Eindruck, dein Tracing sei defekt, obwohl nur die Tool-Konfiguration nicht abgestimmt ist.

Lösung: Setze OTEL_TRACES_SAMPLER_ARG und Sentrys traces_sample_rate auf denselben Wert, oder nutze Sentry als Tail-based Sampler hinter OTel.

2. Clock Skew zwischen Containern. Traces, die aus Spans verschiedener Container rekonstruiert werden, brauchen konsistente Zeitstempel. Ein Container, dessen Uhr auch nur um 200 ms abweicht, produziert Traces mit überlappenden Spans oder negativer Dauer. Das sieht aus wie ein Instrumentierungsfehler, ist aber ein Infrastrukturproblem.

Lösung: Stelle sicher, dass alle Container die NTP-synchronisierte Uhr des Hosts nutzen. In Kubernetes passiert das automatisch; in reinem Docker Compose nicht - und genau da erwischt es Teams unvorbereitet.

3. Label-Kardinalitätsexplosion durch Trace-IDs in Prometheus. Wenn du versehentlich eine Trace-ID, Request-ID oder User-ID als Prometheus-Label aufnimmst, erzeugst du eine eigene Zeitreihe pro Request. Prometheus wird quälend langsam und läuft irgendwann in ein Out-of-Memory.

Lösung: Auditiere dein Label-Set vor dem Produktions-Deployment. Jedes Label mit unbegrenztem Wertebereich muss im Collector oder in der Metrik-Schicht der Anwendung entfernt werden, bevor Prometheus es aufnimmt. Der transform-Processor des OTel Collectors kann gezielt Attribute aus Metriken entfernen, bevor sie Prometheus erreichen.


Der Referenz-Stack mit docker-compose

Für lokale Entwicklung und Staging verdrahtet dieses minimale Compose-File alle vier Tools. Passe Ressourcenlimits und Storage-Volumes an, bevor du es in Produktion einsetzt.

services:
  prometheus:
    image: prom/prometheus:v2.51.0
    volumes:
      - ./config/prometheus.yml:/etc/prometheus/prometheus.yml
      - ./config/rules:/etc/prometheus/rules
    ports: ["9090:9090"]

  grafana:
    image: grafana/grafana:10.4.2
    environment:
      - GF_INSTALL_PLUGINS=grafana-sentry-datasource
    ports: ["3001:3000"]
    depends_on: [prometheus, loki, tempo]

  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.99.0
    volumes:
      - ./config/otel-collector.yaml:/etc/otel/config.yaml
    command: ["--config", "/etc/otel/config.yaml"]
    ports: ["4317:4317", "4318:4318", "8888:8888"]

  tempo:
    image: grafana/tempo:2.4.1
    command: ["-config.file=/etc/tempo.yaml"]
    volumes:
      - ./config/tempo.yaml:/etc/tempo.yaml

  loki:
    image: grafana/loki:2.9.7
    ports: ["3100:3100"]

Es gleich beim ersten Mal richtig machen

Diesen Stack von Grund auf zu bauen kostet zwei bis drei Tage sorgfältiger Konfiguration. Ihn falsch zu bauen - nicht abgestimmtes Sampling, unbegrenzte Label-Kardinalität, isoliertes Alerting - bedeutet Observability-Tooling, das dir beim Debuggen von Produktionsvorfällen nicht wirklich schneller hilft. Der Wert eines verbundenen Stacks zeigt sich erst, wenn du in unter fünf Minuten von einem Alert zur Ursache kommst.

Wenn du eine Symfony- oder Next.js-Anwendung instrumentierst und eine zweite Meinung zu deinem aktuellen Setup möchtest - oder diesen Stack von Anfang an gebaut und validiert brauchst - ist hello@wolf-tech.io der richtige Startpunkt. Observability-Architektur ist auch Teil unserer Beratungen zu Code-Qualität und individueller Anwendungsentwicklung auf wolf-tech.io.