Kotlin Observability: Tracing, Metriken und Logs in der Praxis

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum Observability ein Must-have für Kotlin-Anwendungen ist

\n\n

Stellen Sie sich vor: Ihre Kotlin-Anwendung läuft in der Produktion und plötzlich beschweren sich Benutzer, dass Anfragen 10 Sekunden dauern. Sie schauen in die Logs – Stille. Metriken zeigen 100% CPU-Auslastung, sagen aber nicht, welcher Endpunkt genau träge ist. Kommt Ihnen das bekannt vor?

\n\n

Willkommen in der Welt, in der Observability (Beobachtbarkeit) keine Option, sondern eine Notwendigkeit ist. Im Gegensatz zum traditionellen Monitoring, das nur mit „ja/nein“ auf die Frage „lebt das System?“ antwortet, ermöglicht Observability Fragen zu stellen, die Sie vorher nicht gestellt hätten. Und Kotlin, mit seiner Prägnanz und seinem leistungsstarken Ökosystem, eignet sich ideal für die Einführung dieser Kultur.

\n\n

In diesem Artikel werden wir die drei Säulen der Observability – Tracing, Metriken und Logs – analysieren und zeigen, wie man sie mit Kotlin verbindet. Kein unnötiges Blabla, nur echte Codebeispiele.

\n\n

Tracing: Die Reise einer Anfrage durch Microservices

\n\n

Verteiltes Tracing ist wie ein GPS für Ihre Anfrage. Sie sehen, durch welche Dienste die Anfrage gelaufen ist, wie lange jeder Schritt gedauert hat und wo die Verzögerung aufgetreten ist. Für Kotlin ist der beste Freund in dieser Angelegenheit OpenTelemetry (OTel).

\n\n

Einrichtung von OpenTelemetry in Kotlin

\n\n

Fügen Sie die Abhängigkeiten in build.gradle.kts hinzu:

\n\n
dependencies {\n    implementation("io.opentelemetry:opentelemetry-api:1.36.0")\n    implementation("io.opentelemetry:opentelemetry-sdk:1.36.0")\n    implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.36.0")\n    implementation("io.opentelemetry:opentelemetry-extension-kotlin:1.36.0")\n}
\n\n

Jetzt erstellen wir einen einfachen Tracer:

\n\n
import io.opentelemetry.api.GlobalOpenTelemetry\nimport io.opentelemetry.api.trace.Span\nimport io.opentelemetry.api.trace.Tracer\nimport io.opentelemetry.context.Scope\n\nclass OrderService(private val tracer: Tracer = GlobalOpenTelemetry.getTracer("order-service")) {\n\n    fun processOrder(orderId: String) {\n        val span: Span = tracer.spanBuilder("processOrder")\n            .setAttribute("order.id", orderId)\n            .startSpan()\n\n        span.use { // use() schließt den Span automatisch\n            try {\n                // Simulation der Arbeit\n                Thread.sleep(100)\n                validateOrder(orderId)\n                chargePayment(orderId)\n            } catch (e: Exception) {\n                span.recordException(e)\n                span.setStatus(StatusCode.ERROR, "Order processing failed")\n                throw e\n            }\n        }\n    }\n\n    private fun validateOrder(orderId: String) {\n        val span: Span = tracer.spanBuilder("validateOrder")\n            .setAttribute("order.id", orderId)\n            .startSpan()\n\n        span.use {\n            Thread.sleep(50) // Simulation der Validierung\n        }\n    }\n\n    private fun chargePayment(orderId: String) {\n        val span: Span = tracer.spanBuilder("chargePayment")\n            .setAttribute("order.id", orderId)\n            .startSpan()\n\n        span.use {\n            Thread.sleep(150) // Simulation der Zahlung\n        }\n    }\n}
\n\n

Beachten Sie span.use – dies ist eine Erweiterungsfunktion aus opentelemetry-extension-kotlin, die den Span nach der Ausführung des Blocks automatisch schließt. Die Schönheit von Kotlin in Aktion!

\n\n

Kontextweitergabe durch Koroutinen

\n\n

In Kotlin verwenden wir aktiv Koroutinen. OpenTelemetry harmoniert hervorragend mit ihnen über den Mechanismus Context:

\n\n
import kotlinx.coroutines.withContext\nimport io.opentelemetry.api.trace.Span\nimport io.opentelemetry.context.Context\n\nsuspend fun processOrderAsync(orderId: String) = withContext(Context.current()) {\n    val span = tracer.spanBuilder("processOrderAsync").startSpan()\n    span.use {\n        // Ihre asynchrone Logik\n    }\n}
\n\n

Tracing offenbart das vollständige Bild: Sie sehen, dass 80% der Zeit für die Zahlung aufgewendet wird

Blogs

Buchempfehlungen