Observability in Java: Vom „Warum ist es abgestürzt?“ zum „Ich weiß, was in einer Minute passiert“
\n\nStellen Sie sich vor: Ihre Java-Anwendung läuft in der Produktion. Alles läuft perfekt. Und dann – Peng! – beschweren sich Nutzer, dass die Seite 10 Sekunden zum Laden braucht. Sie schauen auf den Server, durchforsten die Logs und sehen haufenweise INFO, aber keine einzige ERROR. Was tun? Wo suchen?
Beim klassischen Monitoring prüfen Sie: Läuft der Server noch? Wie viel Speicher ist belegt? Ist der Prozess abgestürzt? Das ist aber so, als ob Sie nur prüfen, ob die Kontrollleuchte am Armaturenbrett leuchtet – nützlich, aber nicht genug. Observability (Beobachtbarkeit) ist die nächste Stufe. Es ist die Fähigkeit, den inneren Zustand eines Systems anhand seiner externen Ausgabedaten zu verstehen: Logs, Metriken und Traces.
\n\nIn diesem Artikel zeige ich Ihnen, wie Sie Observability in Java anhand realer Beispiele implementieren. Wir verwenden OpenTelemetry, Micrometer, Spring Boot Actuator und das klassische SLF4J. Sie erfahren, wie Sie Logs, Metriken und Traces zu einem einheitlichen Bild verknüpfen – und nicht einfach nur „alles auf einen Haufen werfen“.
\n\nWas ist Observability und warum ist es mehr als nur „Logs sammeln“?
\n\nObservability stammt aus der Regelungstechnik. Ein System ist beobachtbar, wenn man seinen inneren Zustand anhand seiner Ausgabedaten rekonstruieren kann. In der Java-Welt bedeutet das drei Säulen:
\n\n- \n
- Logs – strukturierte Aufzeichnungen von Ereignissen (Was ist passiert?); \n
- Metriken – numerische Kennzahlen (Wie viele? Wie lange?); \n
- Traces – Aufrufketten (Welchen Weg hat eine Anfrage genommen?). \n
Ohne Observability sind Sie blind. Sie wissen, dass die CPU zu 95% ausgelastet ist, aber nicht, welcher Endpunkt sie belastet. Sie sehen einen Fehler im Log, verstehen aber nicht, welcher Benutzer ihn ausgelöst hat und welche Dienste beteiligt waren. Observability in Java ist kein „einfach eine Bibliothek einbinden“, sondern eine Entwicklungskultur.
\n\nOpenTelemetry: Das universelle Werkzeug für Observability
\n\nFrüher gab es einen Wildwuchs: Zipkin für Tracing, Prometheus für Metriken, ELK für Logs. Jedes mit eigenem SDK. OpenTelemetry (OTel) kam, um sie zu vereinen. Es ist ein Open-Source-Standard zur Erfassung von Telemetriedaten. Es unterstützt Java nativ und lässt sich leicht in Spring Boot, Micronaut und Quarkus integrieren.
\n\nWie funktioniert das? Sie fügen einen Agenten oder ein SDK in Ihre Java-Anwendung ein, und OTel fängt automatisch HTTP-Anfragen, Datenbankaufrufe und Nachrichtenwarteschlangen ab. Sie müssen nicht für jedes Tool separat Code schreiben.
\n\nBeispiel: OpenTelemetry in Spring Boot konfigurieren
\n\nFügen Sie die Abhängigkeit in Ihre pom.xml ein:
<dependency>\n <groupId>io.opentelemetry</groupId>\n <artifactId>opentelemetry-api</artifactId>\n <version>1.40.0</version>\n</dependency>\n<dependency>\n <groupId>io.opentelemetry</groupId>\n <artifactId>opentelemetry-sdk</artifactId>\n <version>1.40.0</version>\n</dependency>\n<dependency>\n <groupId>io.opentelemetry.instrumentation</groupId>\n <artifactId>opentelemetry-spring-boot-starter</artifactId>\n <version>2.6.0</version>\n</dependency>\n\nGeben Sie nun in der Konfiguration (application.yml) den Exporter an, z. B. für Jaeger oder einen OTLP-Collector:
otel:\n exporter:\n otlp:\n endpoint: http://localhost:4317\n traces:\n exporter: otlp\n metrics:\n exporter: otlp\n logs:\n exporter: otlp\n\nVoilà! Jede HTTP-Anfrage an Ihren Controller erstellt nun automatisch einen Trace. Sie können sehen, wie lange jeder Datenbankaufruf, jeder externe API-Aufruf oder einfach eine Verzögerung im Thread gedauert hat.
\n\nTracing in Java: So finden Sie den „Flaschenhals“
\n\nTracing ist wohl der mächtigste Teil der Observability. Wenn Ihre Anwendung aus Dutzenden von Microservices besteht, sind Traces die einzige Möglichkeit zu verstehen, warum eine Anfrage 2 Sekunden dauert, obwohl jeder Service für sich schnell ist.
\n\nMit OpenTelemetry können Sie sehen, welcher Service die meiste Zeit verbraucht, wo Wartezeiten bei Datenbankabfragen auftreten und ob ein externer API-Aufruf das Problem verursacht. Das ist wie ein Röntgenblick für Ihre verteilte Architektur.
\n\nBeispiel: Eigenen Span in Java erstellen
\n\nManchmal reicht die automatische Instrumentierung nicht aus. Sie möchten vielleicht einen bestimmten Codeabschnitt manuell tracken. So geht's:
\n\nimport io.opentelemetry.api.trace.Span;\nimport io.opentelemetry.api.trace.Tracer;\n\n@Autowired\nprivate Tracer tracer;\n\npublic void meineMethode() {\n Span span = tracer.spanBuilder(\"meineOperation\")\n .setAttribute(\"benutzer.id\", userId)\n .startSpan();\n try (var ignored = span.makeCurrent()) {\n // Ihr Code hier\n Thread.sleep(100);\n } catch (Exception e) {\n span.recordException(e);\n throw e;\n } finally {\n span.end();\n }\n}\n\nSo können Sie jede wichtige Operation in Ihrer Anwendung tracken und mit Metriken und Logs verknüpfen.
\n\nMetriken in Java: Was Sie wirklich messen sollten
\n\nMetriken sind die numerische Grundlage der Observability. Aber nicht jede Metrik ist nützlich. Konzentrieren Sie sich auf die „Golden Signals“: Latenz, Traffic, Fehlerrate und Auslastung. Mit Micrometer und Spring Boot Actuator erhalten Sie diese Metriken praktisch kostenlos.
\n\nFügen Sie einfach die Actuator-Abhängigkeit hinzu, und Sie erhalten Endpunkte wie /actuator/metrics, /actuator/health und /actuator/prometheus. Prometheus kann diese Daten dann abrufen und in Grafana visualisieren.
Beispiel: Benutzerdefinierte Metrik mit Micrometer
\n\nimport io.micrometer.core.instrument.MeterRegistry;\nimport io.micrometer.core.instrument.Timer;\n\n@Service\npublic class BestellService {\n private final Timer bestellTimer;\n\n public BestellService(MeterRegistry registry) {\n this.bestellTimer = registry.timer(\"bestellung.dauer\");\n }\n\n public void bestellungErstellen() {\n bestellTimer.record(() -> {\n // Bestelllogik hier\n });\n }\n}\n\nSo messen Sie genau die Dauer Ihrer Geschäftslogik und können Alarme auslösen, wenn die Latenz steigt.
\n\nLogs in Java: Strukturiert und kontextbezogen
\n\nLogs allein sind nicht genug. Sie müssen strukturiert sein und Kontext enthalten. Verwenden Sie SLF4J mit Logback oder Log4j2 und fügen Sie Tracing-IDs hinzu. So können Sie einen Logeintrag mit einem bestimmten Trace verknüpfen.
\n\nMit OpenTelemetry wird automatisch eine traceId und spanId in die MDC (Mapped Diagnostic Context) eingefügt. Konfigurieren Sie Ihr Logging-Muster, um diese IDs auszugeben:
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId},%X{spanId}] %msg%n</pattern>\n\nJetzt können Sie in Ihrem Log-Analyse-Tool (z. B. Kibana) nach einer bestimmten traceId suchen und alle Logs sehen, die zu einer Anfrage gehören.
Fazit: Observability ist kein Projekt, sondern eine Reise
\n\nObservability in Java ist kein einmaliges Setup. Es ist eine kontinuierliche Verbesserung. Fangen Sie klein an: Integrieren Sie OpenTelemetry, sammeln Sie die grundlegenden Metriken und Logs. Erweitern Sie dann nach und nach Ihre Traces und benutzerdefinierten Metriken.
\n\nDer größte Fehler? Zu viel auf einmal zu wollen. Beginnen Sie mit einem Service, machen Sie ihn beobachtbar, und lernen Sie daraus. Observability ist kein Tool – es ist eine Denkweise. Und sie wird Ihnen helfen, von „Warum ist es abgestürzt?“ zu „Ich weiß, was in einer Minute passiert“ zu gelangen.