Lab 08

Observability

Logs, Metriken und Traces für einen realen Fehlerpfad verbinden.

LoggingMetricsTracingRunbook
SchwierigkeitMittel
Dauer60–90 Min
LernstufeStufe 6 – Praxislabor und Capstone
Arbeitsweise: Erst den Vorher-Code öffnen, dann Analyse lesen, anschließend die Nachher-Lösung und den Test vergleichen. Alle Abschnitte sind standardmäßig geschlossen.

Workshop-Slice

Dieses Lab zeigt einen konkreten Enterprise-Fehler mit Vorher/Nachher-Code. Die Beispiele sind bewusst fachlich benannt, damit du Architekturentscheidungen und nicht nur Syntax übst.

Lernziel

Du machst einen Fehlerfall so beobachtbar, dass Support und Betrieb ihn ohne Debugger nachvollziehen können.

Ausgangslage

Ein sporadischer Billing-Fehler tritt nur unter Last auf. Logs enthalten Freitext ohne Order-ID, Trace-ID oder Metriken.

Vorher: problematischer Code

Freitext-Logs helfen lokal, aber nicht im zentralen Betrieb.

BillingAdapter.javaJAVA
try {
    legacy.createInvoice(request);
    log.info("invoice ok");
} catch (Exception e) {
    log.error("failed");
    throw e;
}
Analyse: Was ist daran schlecht?
  • Keine Order-ID im Log.
  • Kein strukturierter Fehlercode.
  • Kein Timer für Legacy-Latenz.
  • Trace-ID fehlt im Fehlervertrag.
Nachher: bessere Lösung

Die Zielversion verbindet strukturierte Logs, Metriken und Trace-Kontext.

BillingAdapter.javaJAVA
class BillingAdapter {
    private final ObservationRegistry observationRegistry;
    private final MeterRegistry meterRegistry;

    InvoiceId createInvoice(ConfirmedOrder order) {
        return Observation.createNotStarted("billing.soap.create_invoice", observationRegistry)
            .lowCardinalityKeyValue("system", "legacy-billing")
            .highCardinalityKeyValue("order.id", order.orderId().value())
            .observe(() -> callLegacy(order));
    }

    private InvoiceId callLegacy(ConfirmedOrder order) {
        long started = System.nanoTime();
        try {
            InvoiceId invoiceId = soap.createInvoice(order);
            meterRegistry.counter("billing.invoice.created").increment();
            log.info("invoice_created orderId={} invoiceId={}", order.orderId().value(), invoiceId.value());
            return invoiceId;
        } catch (LegacyBillingUnavailable ex) {
            meterRegistry.counter("billing.invoice.failed", "reason", "legacy_unavailable").increment();
            log.warn("invoice_failed orderId={} errorCode={} message={}",
                order.orderId().value(), "BILLING_LEGACY_UNAVAILABLE", ex.getMessage());
            throw ex;
        } finally {
            meterRegistry.timer("billing.soap.duration").record(System.nanoTime() - started, TimeUnit.NANOSECONDS);
        }
    }
}
runbook-fragment.mdMARKDOWN
### Fehlercode BILLING_LEGACY_UNAVAILABLE
1. Trace-ID aus ProblemDetails kopieren.
2. In Logs nach `orderId` und `errorCode=BILLING_LEGACY_UNAVAILABLE` suchen.
3. Dashboard `billing.soap.duration` prüfen.
4. Falls `billing.invoice.failed{reason="legacy_unavailable"}` steigt: Legacy-Billing-Eskalation starten.
Test / Prüfnachweis

Dieser Abschnitt zeigt, wie du die Verbesserung nachweist. Es ist bewusst kein reiner Happy-Path-Test, sondern prüft ein Risiko aus dem Vorher-Teil.

BillingMetricsTest.javaJAVA
@Test
void failedLegacyCallIncrementsFailureMetric() {
    adapter.createInvoice(orderThatFails());

    assertThat(meterRegistry.counter("billing.invoice.failed", "reason", "legacy_unavailable").count())
        .isEqualTo(1.0);
}
Typische Fehler
  • Logmeldungen ohne fachliche ID.
  • Hohe Kardinalität in Metriklabels.
  • Trace-ID nicht bis API-Fehler durchreichen.
  • Runbook erst nach Incident schreiben.
Deep-Learning-Bezug

Die Links führen zum ausführlichen Inhalt; die Deep-Learning-Seite bleibt nur die Lernlandkarte und kopiert den Inhalt nicht doppelt.

Prüfcheckliste
  • Jeder Fehlerpfad hat orderId oder fachliche Korrelations-ID.
  • Metriken haben kontrollierte Labels.
  • Runbook beschreibt Suche und Eskalation.
  • Trace-ID taucht in API und Logs auf.
⌂ Cockpit