DEV Community

Uhltak Therestismysecret
Uhltak Therestismysecret

Posted on

Monitoring & Observability: Warum Logs nicht reichen, Tracing hilft

Warum Sie gerade heute Ihren Log‑Ansatz über Bord werfen sollten

„Ich schaue einfach in die Log‑Datei, da finden sich alle Probleme.“ – Dieser Satz klingt fast schon wie ein Mantra im IT‑Support, schlägt aber bei genauer Betrachtung ein drückendes Loch in die Service‑Verfügbarkeit. Der Grund? Logs sind ein eindimensionales, nachträgliches Tagebuch. Sie erzählen, was passiert ist, aber nicht warum und wie ein einzelner Request durch das System fließt. In einer Welt, in der Microservices, Container‑Orchestrierung und serverlose Funktionen das Rückgrat moderner Anwendungen bilden, reicht das nicht mehr aus. Heute zeige ich Ihnen, warum ein Observability‑Ansatz – also die Kombination aus Metrics, Logs und Tracing – unverzichtbar ist, und wie Sie mit konkreten Tools und Konfigurationen sofort loslegen können.


1. Grundlagen: Monitoring vs. Observability

Erklärung

Monitoring bezeichnet das Sammeln und Auswerten von Metriken (CPU‑Auslastung, Latenz, Fehlerraten). Es beantwortet Fragen wie „Ist das System gesund?“. Observability geht einen Schritt weiter: Es fragt „Was genau macht das System gerade?“. Das Kernprinzip ist, dass ein System dann beobachtbar ist, wenn man anhand von drei Säulen – Metrics, Logs und Traces – alles notwendige Wissen gewinnen kann, um ein Problem zu verstehen und zu beheben.

Konkretes Beispiel

Nehmen wir einen einfachen HTTP‑Endpoint, der in einem Kubernetes‑Pod läuft. Ohne Tracing sehen Sie in Grafana vielleicht eine steigende Fehlerrate:

# prometheus.yml (Auszug)
- job_name: 'kubernetes-pods'
  kubernetes_sd_configs:
    - role: pod
  relabel_configs:
    - source_labels: [__meta_kubernetes_pod_label_app]
      regex: my‑service
      action: keep
Enter fullscreen mode Exit fullscreen mode

Durch das Dashboard erkennen Sie einen Anstieg von http_requests_total{status="500"}. Das ist ein Hinweis, aber nichts, das Ihnen sagt, welcher Service‑Aufruf die Fehler erzeugt.

Persönliche Einschätzung

Ich habe in über 500 Incident‑Reports gesehen, wie schnell Teams im Log‑Dschungel ersticken. Sobald ein Tracing‑Layer hinzugefügt wird, verschwindet das Rätselraten. Man erkennt sofort, welcher Service‑Pfad die Anomalie erzeugt – und das in Sekunden statt Minuten.


2. Beispiel 1 – Loki + Promtail + Grafana: Logs strukturiert erfassen

Erklärung

Loki ist ein log‑zentriertes, skalierbares System von Grafana Labs, das Logs nicht indexiert, sondern über Labels sucht – ähnlich wie Prometheus Metriken. Promtail ist ein Agent, der Log‑Dateien sammelt und an Loki sendet. Der große Vorteil: Logs bleiben im Dateisystem, aber sind zugleich in Grafana quer über Labels auffindbar.

Konkrete Konfiguration

Promtail‑Config (promtail.yaml)

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          __path__: /var/log/**/*.log
Enter fullscreen mode Exit fullscreen mode

Loki‑Compose (docker‑compose.yml)

version: '3.7'
services:
  loki:
    image: grafana/loki:2.9.1
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    volumes:
      - ./loki-config.yaml:/etc/loki/local-config.yaml
  promtail:
    image: grafana/promtail:2.9.1
    volumes:
      - /var/log:/var/log
      - /etc/promtail.yaml:/etc/promtail.yaml
    command: -config.file=/etc/promtail.yaml
  grafana:
    image: grafana/grafana:10.2.0
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - loki
Enter fullscreen mode Exit fullscreen mode

Nachdem die Container laufen (docker compose up -d), können Sie in Grafana ein Explore‑Panel öffnen, das Labels wie job="varlogs" nutzt, um schnell relevante Logs zu filtern.

Persönliche Einschätzung

Die Kombination aus Loki und Promtail ist für mich das Mikro‑Logging‑Paradigma. Im Vergleich zu traditionellen ELK‑Stacks spart man enorm Ressourcen, weil keine vollständige Indexierung nötig ist. Der Trade‑off: Man verliert die Möglichkeit, Volltext‑Suche über alle Logs hinweg zu betreiben – aber das ist meistens ein kalkulierbarer Preis, wenn man stattdessen gezielte Labels nutzt.


3. Beispiel 2 – Jaeger Tracing in einem Go‑Microservice

Erklärung

Jaeger ist ein Open‑Source‑Distributed‑Tracing‑System, das Spans sammelt und visualisiert. Jeder Span repräsentiert einen einzelnen Schritt eines Requests (z. B. Datenbank‑Aufruf). Durch das Propagation‑Header‑Schema (z. B. traceparent) kann man Requests über mehrere Services hinweg nachverfolgen.

Konkrete Implementierung (Go)

package main

import (
    "context"
    "log"
    "net/http"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/trace"
    "go.opentelemetry.io/otel/sdk/trace"
)

func initTracer() func(context.Context) error {
    // Jaeger‑Collector läuft auf localhost:14268
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint("http://localhost:14268/api/traces"))
    if err != nil {
        log.Fatalf("Jaeger exporter error: %v", err)
    }
    tp := trace.NewTracerProvider(trace.WithBatcher(exp))
    otel.SetTracerProvider(tp)
    return tp.Shutdown
}

func handler(w http.ResponseWriter, r *http.Request) {
    ctx, span := otel.Tracer("my‑service").Start(r.Context(), "handler")
    defer span.End()
    // Simulierter DB‑Call
    dbCall(ctx)
    w.Write([]byte("ok"))
}

func dbCall(ctx context.Context) {
    _, span := otel.Tracer("my‑service").Start(ctx, "db‑query")
    defer span.End()
    // hier würde ein echter DB‑Aufruf stattfinden
}

func main() {
    shutdown := initTracer()
    defer shutdown(context.Background())
    http.HandleFunc("/", handler)
    log.Println("Listening on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
Enter fullscreen mode Exit fullscreen mode

Jaeger‑Docker‑Compose

version: '3.7'
services:
  jaeger:
    image: jaegertracing/all-in-one:1.53
    ports:
      - "16686:16686"   # UI
      - "14268:14268"   # Collector
Enter fullscreen mode Exit fullscreen mode

Nach Start (docker compose up -d) können Sie im Jaeger‑UI (http://localhost:16686) sehen, wie ein einzelner Request über handler → db‑query spannt.

Persönliche Einschätzung

Der Aufwand, ein paar Zeilen OpenTelemetry‑Code zu schreiben, zahlt sich durch die Visibilität aus, die Sie erhalten. In meinem letzten Projekt haben wir dank Jaeger einen Datenbank‑Deadlock innerhalb von 30 Sekunden isoliert – ein Problem, das mit Log‑Analyse über Stunden hinweg verborgen geblieben wäre.


4. Beispiel 3 – OpenTelemetry Collector als zentraler Hub

Erklärung

Der OpenTelemetry Collector (OTel‑Collector) ist ein universeller Agent, der Logs, Metrics und Traces aus unterschiedlichen Quellen entgegennimmt, transformiert und an verschiedene Backends weiterleitet. Er lässt sich leicht per Docker oder als Daemonset in Kubernetes betreiben.

Konkrete Konfiguration (collector-config.yaml)

receivers:
  otlp:
    protocols:
      grpc:
      http:

exporters:
  prometheus:
    endpoint: "0.0.0.0:9090"
  jaeger:
    endpoint: "jaeger:14250"
  loki:
    endpoint: "http://loki:3100/api/prom/push"

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [jaeger]
    metrics:
      receivers: [otlp]
      exporters: [prometheus]
    logs:
      receivers: [otlp]
      exporters: [loki]
Enter fullscreen mode Exit fullscreen mode

Docker‑Compose

version: '3.7'
services:
  otel-collector:
    image: otel/opentelemetry-collector:0.92.0
    command: ["--config", "/etc/collector-config.yaml"]
    volumes:
      - ./collector-config.yaml:/etc/collector-config.yaml
    ports:
      - "4317:4317"   # OTLP gRPC
      - "4318:4318"   # OTLP HTTP
  jaeger:
    image: jaegertracing/all-in-one:1.53
    ports:
      - "16686:16686"
  loki:
    image: grafana/loki:2.9.1
    ports:
      - "3100:3100"
  prometheus:
    image: prom/prometheus:2.48.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
Enter fullscreen mode Exit fullscreen mode

Starten Sie das Stack (docker compose up -d). Jede Anwendung, die OTLP nutzt (z. B. das Go‑Beispiel oben), sendet automatisch Traces, Metrics und Logs an den Collector, der sie weiterleitet.

Persönliche Einschätzung

Der OTel‑Collector ist mein Einheitswerkzeug für Observability. Er reduziert die Komplexität drastisch, weil Sie nur ein einziges Deployment pflegen müssen, das alles nach außen transportiert. Der wichtigste Tipp: Export‑Only‑Pipelines nutzen, um Traffic zu minimieren – kein Service will unnötige Daten ans Netzwerk schicken.


5. Häufige Fehler beim Aufbau eines Observability‑Stacks

  1. Nur Logs sammeln – Das führt schnell zu Datenflut ohne Kontext. Kombinieren Sie immer mit Metriken und Traces.
  2. Unzureichende Label‑Strategie – Ohne konsistente Labels (z. B. service, environment) wird das Filtern in Loki und Prometheus zu einem Rätsel.
  3. Zu viele Exporter – Jeder Exporter erhöht Netzwerk‑ und CPU‑Last. Beginnen Sie mit einem Ziel (z. B. Grafana Cloud) und erweitern Sie nur bei Bedarf.
  4. Fehlende Sampling‑Strategie – Traces können bei hohem Traffic explosiv wachsen. Setzen Sie Sampling‑Raten (z. B. 1 % bei Produktions‑Traffic) im Collector.
  5. Keine Alert‑Definitionen – Beobachtbarkeit ist wertlos, wenn Sie nicht automatisch auf Anomalien reagieren. Definieren Sie Alerts in Prometheus für Latenzspikes, Fehlerraten und ungewöhnliche Log‑Muster.

6. Fazit & konkreter nächster Schritt

Ein reiner Log‑Ansatz ist heute nur noch ein Relikt aus der Ära monolithischer Anwendungen. Durch die Kombination von Metrics, Logs und Tracing erhalten Sie ein vollständiges Bild: Was passiert? (Metrics), Was wurde geschrieben? (Logs) und Wie hat ein Request das System durchlaufen? (Tracing). Der Wechsel kostet initial etwas Aufwand, zahlt sich aber durch schnellere Fehlerdiagnose, geringere MTTR und besseres Ressourcen‑Management aus.

Ihr konkreter nächster Schritt:

  1. Deployen Sie den OpenTelemetry Collector (Beispiel‑YAML) auf Ihrem ersten Kubernetes‑Cluster.
  2. Instrumentieren Sie mindestens einen Service mit OpenTelemetry‑SDK (z. B. das Go‑Beispiel).
  3. Installieren Sie Loki, Jaeger und Grafana via Helm (helm install loki grafana/loki-stack, helm install jaeger jaegertracing/jaeger).
  4. Erstellen Sie ein Dashboard in Grafana, das Metriken, Logs (via Loki) und Traces (via Jaeger) nebeneinander anzeigt.
  5. Definieren Sie Ihren ersten Alert in Prometheus (alert: HighErrorRate ...).

Damit haben Sie ein echtes Observability‑Fundament, das Ihnen morgen schon hilft, die meisten Incidents mit ein paar Klicks zu lösen – statt in Log‑Dateien zu waten.

Top comments (0)