Por que a observabilidade é indispensável para aplicações Kotlin
\n\nImagine: sua aplicação Kotlin está rodando em produção e, de repente, os usuários reclamam que as requisições estão demorando 10 segundos. Você verifica os logs — silêncio. As métricas mostram 100% de CPU, mas não indicam qual endpoint específico está causando a lentidão. Parece familiar?
\n\nBem-vindo ao mundo onde a observabilidade não é uma opção, mas uma necessidade. Diferente do monitoramento tradicional, que simplesmente responde "sim/não" à pergunta "o sistema está vivo?", a observabilidade permite fazer perguntas que você nem sabia que precisava fazer. E o Kotlin, com sua concisão e ecossistema poderoso, é ideal para adotar essa cultura.
\n\nNeste artigo, vamos explorar os três pilares da observabilidade — rastreamento, métricas e logs — e mostrar como integrá-los ao Kotlin. Sem enrolação, apenas exemplos reais de código.
\n\nRastreamento: a jornada da requisição pelos microsserviços
\n\nO rastreamento distribuído (tracing) é como um GPS para sua requisição. Você vê por quais serviços a requisição passou, quanto tempo cada etapa levou e onde ocorreu a lentidão. Para Kotlin, o melhor aliado nessa tarefa é o OpenTelemetry (OTel).
\n\nConfiguração do OpenTelemetry em Kotlin
\n\nAdicione as dependências no build.gradle.kts:
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\nAgora, vamos criar um rastreador simples:
\n\nimport 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() fecha o span automaticamente\n try {\n // Simulação de trabalho\n Thread.sleep(100)\n validateOrder(orderId)\n chargePayment(orderId)\n } catch (e: Exception) {\n span.recordException(e)\n span.setStatus(StatusCode.ERROR, "Falha no processamento do pedido")\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) // Simulação de validação\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) // Simulação de pagamento\n }\n }\n}\n\nObserve o span.use — é uma função de extensão do opentelemetry-extension-kotlin que fecha o span automaticamente após a execução do bloco. A beleza do Kotlin em ação!
Propagação de contexto com corrotinas
\n\nEm Kotlin, usamos corrotinas ativamente. O OpenTelemetry se integra perfeitamente a elas através do mecanismo de Context:
\n\nimport 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 // Sua lógica assíncrona\n }\n}\n\nO rastreamento revela o panorama completo: você vê que 80% do tempo é gasto no pagamento