Kotlin Observability : tracing, métriques et logs en pratique

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Pourquoi l'observabilité est un must-have pour les applications Kotlin

\n\n

Imaginez : votre application Kotlin tourne en production, et soudain, les utilisateurs se plaignent que les requêtes prennent 10 secondes. Vous consultez les logs — silence. Les métriques montrent 100% CPU, mais ne disent pas quel endpoint exact ralentit. Cela vous dit quelque chose ?

\n\n

Bienvenue dans un monde où l'observabilité n'est pas une option, mais une nécessité. Contrairement à la surveillance traditionnelle, qui répond simplement « oui/non » à la question « le système est-il vivant ? », l'observabilité permet de poser des questions auxquelles vous n'aviez pas pensé auparavant. Et Kotlin, avec sa concision et son écosystème puissant, est parfaitement adapté à l'adoption de cette culture.

\n\n

Dans cet article, nous allons décomposer les trois piliers de l'observabilité — tracing, métriques et logs — et montrer comment les associer à Kotlin. Pas de blabla, uniquement des exemples de code concrets.

\n\n

Tracing : le voyage de la requête à travers les microservices

\n\n

Le tracing distribué est comme un GPS pour votre requête. Vous voyez par quels services la requête est passée, combien de temps chaque étape a pris et où le retard s'est produit. Pour Kotlin, le meilleur allié dans ce domaine est OpenTelemetry (OTel).

\n\n

Configuration d'OpenTelemetry dans Kotlin

\n\n

Ajoutez les dépendances dans build.gradle.kts :

\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

Créons maintenant un traceur simple :

\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() fermera automatiquement le span\n            try {\n                // Simulation de travail\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 de validation\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 de paiement\n        }\n    }\n}
\n\n

Remarquez span.use — c'est une fonction d'extension provenant de opentelemetry-extension-kotlin, qui ferme automatiquement le span après l'exécution du bloc. La beauté de Kotlin en action !

\n\n

Transmission contextuelle via les coroutines

\n\n

Dans Kotlin, nous utilisons activement les coroutines. OpenTelemetry s'entend parfaitement avec elles grâce au mécanisme de Contexte :

\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        // Votre logique asynchrone\n    }\n}
\n\n

Le tracing révèle une image complète : vous voyez que 80% du temps est consacré au paiement

Blogs

Book Recommendations