Observability für REST APIs: Logging, Tracing und Metriken
AI generated
{ }
GET
REST API · Observability · Logging · Tracing · Metriken
Observability für REST APIs
Logging, Tracing und Metriken vollständig umgesetzt

Eine REST API, die in der Produktion läuft, aber nicht beobachtbar ist, ist eine Blackbox. Strukturiertes Logging, Distributed Tracing mit OpenTelemetry und Prometheus-Metriken sind die drei Säulen, mit denen Teams Fehler lokalisieren, Latenzspitzen erklären und Kapazitäten planen können – ohne in Log-Dateien zu graben.

18 Min. Lesezeit OpenTelemetry · Prometheus · Jaeger · Grafana · Loki REST API · PHP · Symfony · Docker

1. Was Observability für REST APIs bedeutet

Der Begriff Observability kommt aus der Regelungstheorie und beschreibt die Fähigkeit, den internen Zustand eines Systems allein durch seine Ausgaben zu verstehen. Für REST APIs bedeutet das konkret: Man soll in der Lage sein, ohne manuelles Debugging zu erkennen, warum ein Request fehlgeschlagen ist, wo die Latenz entstand, welche Downstream-Services betroffen waren und ob es sich um einen Einzelfall oder ein Muster handelt. Die drei Säulen – Logs, Traces und Metriken – ergänzen sich dabei: Metriken zeigen, dass etwas nicht stimmt, Traces zeigen wo das Problem liegt, und Logs erklären warum.

In der Praxis sieht man häufig API-Setups, bei denen Logs als Plaintext in Dateien geschrieben werden, Fehlersuche durch SSH auf den Server und grep durch Logdateien passiert und der Begriff "Tracing" als Optional gilt, das man irgendwann einmal implementiert. Dieses Vorgehen funktioniert für kleine Teams und niedrige Traffic-Volumes, bricht aber zusammen, sobald mehrere Services, mehrere Instanzen oder komplexe Fehlermuster auftreten. Der Invest in einen vollständigen Observability-Stack zahlt sich bereits bei der ersten schwer reproduzierbaren Produktion aus, die ohne Traces und strukturierte Logs Stunden statt Minuten Debugging bedeutet hätte.

Ein weiterer häufig übersehener Aspekt: Observability ist keine Infrastrukturaufgabe, sondern eine API-Design-Aufgabe. Die Instrumentierung muss in den API-Code eingebaut werden – sinnvolle Span-Namen, relevante Attribute, klare Log-Felder. Wer Observability nachträglich als Layer draufsetzt, erhält zwar Daten, aber selten die Daten, die im Incident-Fall wirklich helfen. Der folgende Guide zeigt, wie man REST APIs von Anfang an für vollständige Observability instrumentiert.

2. Strukturiertes Logging: JSON statt Plaintext

Strukturiertes Logging ist die Grundlage jedes Observability-Stacks. Statt menschenlesbarer Plaintext-Zeilen werden Logs als maschinenlesbare JSON-Objekte geschrieben, die direkt indiziert und abgefragt werden können. Jedes Log-Eintrag hat ein definiertes Schema: Zeitstempel, Log-Level, Message, Service-Name, Request-ID, User-ID falls vorhanden, HTTP-Methode, Pfad, Statuscode und Antwortdauer. Mit diesem Schema kann ein Log-Aggregator wie Loki, Elasticsearch oder Datadog die Felder direkt indizieren, sodass Suchen wie "alle fehlgeschlagenen POST-Requests auf /orders in den letzten 5 Minuten" in Millisekunden beantwortet werden – nicht durch Full-Text-Suche in GBs von Text.

In Symfony wird strukturiertes Logging über Monolog konfiguriert. Der JsonFormatter wandelt alle Log-Einträge in JSON um. Kritisch ist das Processor-Konzept: Jeder Processor fügt automatisch Felder zu jedem Log-Eintrag hinzu, ohne dass der Application-Code explizit Felder mitgeben muss. Ein RequestIdProcessor fügt die aktuelle Request-ID hinzu, ein UserProcessor die aktuell authentifizierte User-ID. So enthält jeder Log-Eintrag – egal wo im Code er erzeugt wurde – automatisch den vollständigen Kontext des aktuellen Requests.


# config/packages/monolog.yaml — structured JSON logging for REST API
monolog:
  handlers:
    app:
      type: stream
      path: php://stdout
      level: info
      formatter: monolog.formatter.json
      channels: ['!event']
      processors:
        - Monolog\Processor\UidProcessor
        - App\Log\RequestIdProcessor
        - App\Log\UserContextProcessor

    error:
      type: fingers_crossed
      action_level: error
      handler: error_stream
      channels: ['!event']

    error_stream:
      type: stream
      path: php://stderr
      level: debug
      formatter: monolog.formatter.json

services:
  monolog.formatter.json:
    class: Monolog\Formatter\JsonFormatter
    calls:
      - [includeStacktraces, [true]]

Das Logging-Schema sollte von Anfang an für alle Endpoints einheitlich sein. Ein API-Request-Log enthält mindestens: request_id, method, path, status_code, duration_ms, ip und user_agent. Ein Fehler-Log enthält zusätzlich exception_class, exception_message und – nur in Nicht-Produktionsumgebungen – stack_trace. Niemals sollten sensible Daten wie Passwörter, API-Keys oder vollständige Request-Bodies in Logs landen. Ein Log-Sanitizer-Processor, der bekannte sensible Felder aus dem Context entfernt, ist in jedem produktiven System Pflicht.

3. Correlation IDs und Request-Kontext

Eine Correlation ID (auch Trace-ID oder Request-ID genannt) ist ein eindeutiger Bezeichner, der einem einzelnen eingehenden Request zugewiesen wird und durch alle beteiligten Services und Log-Einträge propagiert wird. Mit einer Correlation ID kann man alle Log-Einträge, die zu einem einzigen Request gehören, sofort aus Millionen von Einträgen herausfiltern – auch wenn der Request fünf verschiedene Microservices durchlaufen hat. Das ist der entscheidende Unterschied zwischen einem Logging-System und einem Observability-System.

Die Implementierung folgt einem klaren Muster: Beim Eingang eines Requests prüft ein Middleware-Layer, ob ein X-Request-ID-Header vorhanden ist. Falls ja, wird dieser Wert übernommen (das ermöglicht Client-seitige Correlation). Falls nein, wird eine neue UUID generiert. Die ID wird in einem Request-scoped Container gespeichert, von allen Log-Processors abgerufen und an alle ausgehenden HTTP-Requests via Header weitergegeben. Die Antwort enthält den Header ebenfalls, sodass Frontend-Teams oder externe Konsumenten bei Support-Anfragen ihre Request-ID mitliefern können.


# App\EventListener\RequestIdListener — assigns and propagates Correlation IDs

namespace App\EventListener;

use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

#[AsEventListener(event: RequestEvent::class, priority: 200)]
#[AsEventListener(event: ResponseEvent::class, priority: -200)]
final class RequestIdListener
{
    private string $requestId = '';

    public function __construct(
        private readonly RequestIdStorage $storage
    ) {}

    public function onRequestEvent(RequestEvent $event): void
    {
        $request = $event->getRequest();
        $this->requestId = $request->headers->get('X-Request-ID')
            ?? $this->generateId();
        $this->storage->set($this->requestId);
        $request->headers->set('X-Request-ID', $this->requestId);
    }

    public function onResponseEvent(ResponseEvent $event): void
    {
        $event->getResponse()->headers->set(
            'X-Request-ID',
            $this->requestId
        );
    }

    private function generateId(): string
    {
        return sprintf('%s-%s', date('Ymd'), bin2hex(random_bytes(8)));
    }
}

4. Distributed Tracing mit OpenTelemetry

Distributed Tracing geht über Correlation IDs hinaus: Statt nur Log-Zeilen miteinander zu verknüpfen, wird der gesamte Ausführungsbaum eines Requests als hierarchische Baumstruktur – ein Trace – aufgezeichnet. Jeder Trace besteht aus Spans: einem Root-Span für den eingehenden HTTP-Request und Kind-Spans für jeden Datenbank-Query, externe HTTP-Aufruf, Cache-Lookup oder Message-Queue-Publish. In einem Trace-Viewer wie Jaeger oder Zipkin sieht man sofort, welcher Span wie viel Zeit verbraucht hat und wo im Ausführungsbaum ein Fehler aufgetreten ist.

OpenTelemetry ist der offene Standard, der Tracing, Logging und Metriken unter einem Dach vereint und Vendor-Lock-in verhindert. Die PHP-Bibliothek open-telemetry/opentelemetry-php bietet Auto-Instrumentation für gängige Frameworks. Für Symfony bedeutet das: HTTP-Requests, Doctrine-Queries und ausgehende HTTP-Calls werden automatisch instrumentiert, ohne dass jede einzelne Funktion manuell annotiert werden muss. Eigene Spans für kritische Geschäftslogik fügt man mit wenigen Zeilen Code hinzu.


# docker-compose.yml — OpenTelemetry Collector + Jaeger für lokale Entwicklung
services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:latest
    volumes:
      - ./otel-collector-config.yaml:/etc/otel/config.yaml
    command: ["--config=/etc/otel/config.yaml"]
    ports:
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
    depends_on:
      - jaeger

  jaeger:
    image: jaegertracing/all-in-one:latest
    environment:
      COLLECTOR_OTLP_ENABLED: "true"
    ports:
      - "16686:16686"  # Jaeger UI
      - "14250:14250"  # gRPC collector

# otel-collector-config.yaml
# receivers:
#   otlp:
#     protocols:
#       grpc:
#       http:
# exporters:
#   jaeger:
#     endpoint: jaeger:14250
#     tls:
#       insecure: true
#   prometheus:
#     endpoint: "0.0.0.0:8889"
# service:
#   pipelines:
#     traces:
#       receivers: [otlp]
#       exporters: [jaeger]
#     metrics:
#       receivers: [otlp]
#       exporters: [prometheus]

Eigene Spans für fachliche Operationen hinzuzufügen ist in OpenTelemetry einfach und zahlt sich bei der Fehlersuche aus. Ein Span für den gesamten Order-Checkout-Prozess, mit Attributen wie order.total, order.item_count und user.id, macht es möglich, im Trace-Viewer sofort zu sehen, welche Order betroffen war und ob es sich um einen Muster-Fehler bei bestimmten Bestellmengen handelt. Span-Attribute sind die strukturierten Felder des Tracing-Systems – sie sollten denselben Standards folgen wie die strukturierten Felder im Logging: konsistent, dokumentiert und niemals sensible Daten enthaltend.

5. Metriken mit Prometheus und Grafana

Metriken sind numerische Zeitreihen-Daten, die den Zustand eines Systems über die Zeit abbilden. Während Logs einzelne Ereignisse beschreiben und Traces einzelne Requests, zeigen Metriken Trends und Aggregate: Wie viele Requests pro Sekunde verarbeitet der Service? Welcher Anteil gibt 5xx-Fehler zurück? Wie ist die Latenz-Verteilung über alle Requests in den letzten 24 Stunden? Diese Fragen lassen sich mit Logs und Traces zwar beantworten, aber nicht effizient – Metriken sind dafür der richtige Kanal.

Für REST APIs sind vier Metriken-Typen besonders relevant: Counter für Gesamtzahlen (Requests gesamt, Fehler gesamt), Gauge für aktuelle Zustände (aktive Verbindungen, Queue-Länge), Histogram für Latenz-Verteilungen (Response-Time-Buckets) und Summary für vorab berechnete Quantile. Das RED-Method-Framework empfiehlt für jeden Service drei Metriken zu tracken: Rate (Requests pro Sekunde), Errors (Fehlerrate) und Duration (Latenz-Verteilung). Diese drei Kennzahlen reichen aus, um 90% aller Production-Incidents initial zu klassifizieren.


# Prometheus metrics endpoint — custom REST API metrics in PHP/Symfony

# Install: composer require promphp/prometheus_client_php

namespace App\Metrics;

use Prometheus\CollectorRegistry;
use Prometheus\Counter;
use Prometheus\Histogram;
use Symfony\Component\HttpKernel\Event\TerminateEvent;

final class ApiMetricsCollector
{
    private Counter $requestsTotal;
    private Counter $errorsTotal;
    private Histogram $requestDuration;

    public function __construct(
        private readonly CollectorRegistry $registry
    ) {
        $this->requestsTotal = $registry->getOrRegisterCounter(
            'api', 'requests_total',
            'Total number of API requests',
            ['method', 'endpoint', 'status_code']
        );

        $this->errorsTotal = $registry->getOrRegisterCounter(
            'api', 'errors_total',
            'Total number of API errors',
            ['method', 'endpoint', 'error_type']
        );

        // Latency buckets optimized for REST APIs (ms)
        $this->requestDuration = $registry->getOrRegisterHistogram(
            'api', 'request_duration_seconds',
            'API request duration in seconds',
            ['method', 'endpoint'],
            [0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
        );
    }

    public function recordRequest(
        string $method,
        string $endpoint,
        int $statusCode,
        float $durationSeconds
    ): void {
        $this->requestsTotal->inc([$method, $endpoint, (string)$statusCode]);
        $this->requestDuration->observe($durationSeconds, [$method, $endpoint]);

        if ($statusCode >= 500) {
            $this->errorsTotal->inc([$method, $endpoint, 'server_error']);
        } elseif ($statusCode >= 400) {
            $this->errorsTotal->inc([$method, $endpoint, 'client_error']);
        }
    }
}

6. Alerting und SLO-Definitionen

Service Level Objectives (SLOs) sind die Brücke zwischen Observability-Daten und geschäftlichen Anforderungen. Ein SLO definiert, welche Qualität ein Service liefern muss – zum Beispiel "99,9% aller Requests müssen innerhalb von 500ms beantwortet werden" oder "Die Fehlerrate darf 0,1% nicht überschreiten". Alerting-Regeln in Prometheus lösen eine Benachrichtigung aus, wenn das SLO-Budget erschöpft zu werden droht – nicht erst, wenn der Service vollständig ausgefallen ist. Dieses Konzept heißt Error Budget: Ein 99,9%-SLO erlaubt 43,8 Minuten Downtime pro Monat. Alerting setzt ein, wenn das Budget zu schnell verbraucht wird.

Alerting-Regeln in Prometheus sind YAML-Dateien, die auf PromQL-Ausdrücken basieren. Eine gute Alerting-Strategie für REST APIs unterscheidet drei Ebenen: Symptom-basierte Alerts (hohe Fehlerrate, hohe Latenz – das, was Nutzer direkt spüren), Saturation-Alerts (Datenbankverbindungen erschöpft, Speicher kritisch) und Cause-basierte Alerts nur sparsam, weil sie zu viel Rauschen erzeugen. Die häufigste Fehlerquelle beim Alerting: zu viele Alerts, zu niedrige Schwellenwerte, Alert-Fatigue. Die Regel: Jeder Alert muss eine klare Handlungsanweisung haben und von einem Menschen bearbeitet werden können.

7. Typische Fehlerszenarien und wie man sie findet

Observability hat keinen Wert, wenn das Team nicht weiß, wie man die Werkzeuge in einer konkreten Fehlersuche einsetzt. Das typische Vorgehen im Incident: Prometheus-Dashboard zeigt erhöhte Fehlerrate → Grafana-Alert löst aus → On-Call-Engineer öffnet Grafana, sieht den betroffenen Endpoint und den Zeitraum → sucht in Loki nach Logs mit diesem Endpoint und Statuscode 500 → findet die Correlation-ID des ersten fehlgeschlagenen Requests → öffnet Jaeger mit dieser Trace-ID → sieht im Trace-Tree, dass ein Datenbankquery plötzlich 8 Sekunden statt 50ms dauert → prüft Datenbankmetriken → findet fehlenden Index nach einem kürzlichen Schema-Migration. Dieser Prozess dauert mit vollständiger Observability 5–10 Minuten statt Stunden.


# Prometheus alert rules — REST API SLO-based alerting
# prometheus/alerts/api.yml

groups:
  - name: api_slo
    rules:
      # Alert when error rate exceeds 1% over 5 minutes
      - alert: ApiHighErrorRate
        expr: |
          (
            sum(rate(api_errors_total[5m]))
            /
            sum(rate(api_requests_total[5m]))
          ) > 0.01
        for: 2m
        labels:
          severity: warning
          team: backend
        annotations:
          summary: "API error rate above 1%"
          description: >
            Error rate is { { printf "%.2f" $value | humanizePercentage } }
            over the last 5 minutes. SLO budget at risk.
          runbook_url: "https://wiki.mironsoft.de/runbooks/api-high-error-rate"

      # Alert when p95 latency exceeds 500ms
      - alert: ApiHighLatency
        expr: |
          histogram_quantile(0.95,
            sum(rate(api_request_duration_seconds_bucket[5m])) by (le, endpoint)
          ) > 0.5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "API p95 latency above 500ms"
          description: "Endpoint { { $labels.endpoint } } p95 = { { $value | humanizeDuration } }"

8. Observability-Tools im Vergleich

Der Markt für Observability-Tools ist groß und unübersichtlich. Die Wahl zwischen einem vollständig gemanagten SaaS-Stack und einem selbst gehosteten Open-Source-Stack hat direkte Auswirkungen auf Kosten, Datenschutz und Betriebsaufwand. Die folgende Tabelle gibt einen strukturierten Überblick über die häufigsten Tool-Kombinationen für REST API Observability.

Kategorie Open Source (Self-hosted) Managed SaaS Empfehlung
Logs Loki + Grafana Datadog, Logtail, Axiom Loki für DSGVO-kritische Setups
Traces Jaeger, Tempo Honeycomb, Datadog APM Tempo + Grafana (Grafana-Stack)
Metriken Prometheus + Grafana Datadog, New Relic, Grafana Cloud Prometheus für volle Kontrolle
All-in-One Grafana Stack (Loki+Tempo+Prom) Datadog, Dynatrace Grafana Stack als Standard
Instrumentierung OpenTelemetry (offen) Vendor-eigene Agents OpenTelemetry (kein Vendor Lock-in)

9. Zusammenfassung

Observability für REST APIs ist kein Add-on, das man nach dem Launch ergänzt. Strukturiertes Logging mit einheitlichem Schema, Correlation IDs die durch alle Services propagiert werden, Distributed Tracing mit OpenTelemetry und Prometheus-Metriken nach dem RED-Method-Framework – diese vier Komponenten bilden einen Stack, der im Incident-Fall die mittlere Fehlersuche von Stunden auf Minuten reduziert. Der entscheidende Punkt: Die Instrumentierung muss im Code verankert sein, nicht als externer Layer aufgesetzt. Sinnvolle Span-Namen, relevante Attribute und ein klares Log-Schema sind Architekturentscheidungen, keine Betriebsaufgaben.

Der empfohlene Stack für neue Projekte: Symfony mit Monolog und JsonFormatter für Logs, OpenTelemetry PHP SDK für Traces mit Jaeger oder Grafana Tempo als Backend, Prometheus mit dem php-prometheus-client für Metriken und Grafana als einheitliches Dashboard-Frontend. Alle drei Datenquellen können in Grafana korreliert werden – ein Klick auf einen Latenzspike in der Metrik öffnet die zugehörigen Logs und Traces im selben Zeitfenster.

Observability für REST APIs — Das Wichtigste auf einen Blick

Strukturiertes Logging

JSON-Format mit einheitlichem Schema, Correlation-ID-Processor und Log-Sanitizer für sensible Felder. Monolog JsonFormatter als Standard.

Distributed Tracing

OpenTelemetry PHP SDK für Vendor-neutrale Instrumentierung. Jaeger oder Grafana Tempo als Backend. Eigene Spans für fachliche Operationen.

Metriken (RED Method)

Rate, Errors, Duration per Endpoint. Prometheus Histograms für Latenz-Verteilungen. Grafana-Dashboards mit SLO-Burnrate-Alerts.

Alerting

Symptom-basierte Alerts (Fehlerrate, Latenz). Klare Runbooks für jeden Alert. Error-Budget-basiertes SLO-Alerting statt einfacher Schwellenwerte.

Mironsoft

REST API Observability, Monitoring und Performance-Optimierung

REST APIs die man wirklich beobachten kann?

Wir implementieren vollständige Observability-Stacks für REST APIs – von strukturiertem Logging und Distributed Tracing bis zu Prometheus-Metriken und Grafana-Dashboards mit SLO-Alerting.

Logging-Setup

Strukturiertes JSON-Logging mit Correlation IDs und Log-Aggregation

Tracing-Integration

OpenTelemetry-Integration und Jaeger/Tempo-Backend-Konfiguration

SLO-Alerting

Prometheus-Metriken, Grafana-Dashboards und SLO-basierte Alerts

10. FAQ: Observability für REST APIs

1Unterschied zwischen Monitoring und Observability?
Monitoring prüft bekannte Schwellenwerte. Observability ermöglicht, unbekannte Fragen zu beantworten. Ein observables System erlaubt Diagnosen ohne vorher zu wissen, was man sucht.
2Müssen alle drei Säulen implementiert werden?
Nein, aber Metriken zeigen dass etwas falsch ist, Traces wo, Logs warum. Der Grafana-Stack (Loki + Tempo + Prometheus) implementiert alle drei mit minimalem Aufwand.
3Warum OpenTelemetry statt Vendor-SDK?
OpenTelemetry verhindert Vendor Lock-in. Die Instrumentierung im Code bleibt gleich, nur das Backend kann gewechselt werden – von Jaeger zu Datadog oder Grafana Tempo.
4Performance-Overhead durch Distributed Tracing?
Unter 1-2% bei typischen REST APIs. Bei hohem Traffic: Sampling einsetzen – nur 10% aller Requests vollständig tracen, Fehler und langsame Requests immer.
5Was ist eine Correlation ID?
Eindeutige ID pro Request, durch alle Services propagiert. In Logs, Traces und Response-Header einbetten. Ermöglicht Suche aller Logs zu einem einzigen Request.
6Was ist die RED Method?
Rate (Requests/s), Errors (Fehlerrate) und Duration (Latenz). Diese drei Metriken reichen für 90% aller Production-Incident-Klassifizierungen als Startpunkt.
7Darf ich User-Daten in Logs schreiben?
Nur pseudonymisiert. User-IDs meist unbedenklich. Vollständige Namen, E-Mails oder Zahlungsdaten gehören nicht in Logs. Log-Sanitizer-Processor ist Pflicht.
8Span vs. Log-Eintrag?
Ein Span hat Start- und Endzeit und stellt eine Operation dar. Ein Log-Eintrag ist ein einzelnes Ereignis. Spans sind hierarchisch und zeigen Dauer und Kausalität.
9Guter SLO-Startwert für REST APIs?
99,9% Verfügbarkeit, p95 Latenz unter 500ms, Fehlerrate unter 0,1%. Interne APIs können mit 99,5% starten. Werte an Kritikalität des Services anpassen.
10Alert Fatigue vermeiden?
Nur symptom-basierte Alerts. Jeder Alert braucht ein Runbook. Error-Budget-Alerting statt starrer Schwellenwerte. Wichtigkeit klar kommunizieren: Critical vs. Warning.