---
title: "Observabilidade para Aplicações Kotlin: Logs, Métricas e Traces | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/kotlin-observabilidade/"
markdown_url: "https://kotlin.dev.br/blog/kotlin-observabilidade.MD"
description: "Observabilidade para aplicações Kotlin: como implementar logs, métricas e tracing distribuído. Guia prático com exemplos de código."
date: "2025-08-20"
author: "Karina Melo"
---

# Observabilidade para Aplicações Kotlin: Logs, Métricas e Traces | Kotlin Brasil

Observabilidade para aplicações Kotlin: como implementar logs, métricas e tracing distribuído. Guia prático com exemplos de código.


Colocar código em producao e só o comeco. O verdadeiro desafio e saber o que esta acontecendo com sua aplicação em tempo real. Observabilidade, composta por logs, métricas e tracing distribuído, é o que permite entender, diagnosticar e resolver problemas rapidamente. Vamos ver como implementar observabilidade completa em aplicações Kotlin.

## Os Tres Pilares da Observabilidade

### 1. Logs

Logs registram eventos discretos que acontecem na aplicação. São a forma mais básica de observabilidade, mas quando bem estruturados, são extremamente poderosos.

```kotlin
// Logging estruturado com SLF4J e Kotlin
import org.slf4j.LoggerFactory

class PedidoService(
    private val pedidoRepository: PedidoRepository,
    private val pagamentoGateway: PagamentoGateway
) {
    private val logger = LoggerFactory.getLogger(PedidoService::class.java)

    suspend fun processarPedido(pedidoId: String): Pedido {
        logger.info("Iniciando processamento do pedido",
            kv("pedidoId", pedidoId),
            kv("operacao", "processar_pedido")
        )

        val pedido = pedidoRepository.findById(pedidoId)
            ?: run {
                logger.warn("Pedido nao encontrado",
                    kv("pedidoId", pedidoId)
                )
                throw PedidoNaoEncontradoException(pedidoId)
            }

        return try {
            val resultado = pagamentoGateway.processar(pedido)
            logger.info("Pedido processado com sucesso",
                kv("pedidoId", pedidoId),
                kv("valor", pedido.valor),
                kv("status", resultado.status)
            )
            pedido.copy(status = PedidoStatus.PROCESSADO)
        } catch (e: Exception) {
            logger.error("Erro ao processar pedido",
                kv("pedidoId", pedidoId),
                kv("erro", e.message),
                e
            )
            pedido.copy(status = PedidoStatus.ERRO)
        }
    }

    private fun kv(key: String, value: Any?): net.logstash.logback.argument.StructuredArgument {
        return net.logstash.logback.argument.StructuredArguments.kv(key, value)
    }
}
```

### Logs Estruturados com Logback

Para que logs sejam úteis em producao, eles devem ser estruturados em JSON:

```kotlin
// Extension function para logging idiomatico em Kotlin
inline fun <reified T> T.logger(): Logger {
    return LoggerFactory.getLogger(T::class.java)
}

class OrderProcessor {
    private val log = logger()

    fun process(order: Order) {
        log.info("Processing order {} for customer {}",
            order.id, order.customerId)
    }
}
```

A configuração do Logback (logback-spring.xml) deve usar o LogstashEncoder para producao, gerando logs JSON que podem ser facilmente indexados por ferramentas como Elasticsearch ou Loki.

### 2. Métricas

Métricas são dados numericos agregados ao longo do tempo. Respondem perguntas como "quantas requisicoes por segundo estamos recebendo?" e "qual a latencia media da API?".

```kotlin
// Métricas com Micrometer e Spring Boot
@Configuration
class MetricsConfig(private val meterRegistry: MeterRegistry) {

    @Bean
    fun pedidoCounter(): Counter {
        return Counter.builder("pedidos.total")
            .description("Total de pedidos processados")
            .tag("aplicacao", "pedido-service")
            .register(meterRegistry)
    }

    @Bean
    fun pedidoTimer(): Timer {
        return Timer.builder("pedidos.duracao")
            .description("Duracao do processamento de pedidos")
            .publishPercentiles(0.5, 0.95, 0.99)  // p50, p95, p99
            .register(meterRegistry)
    }
}

@Service
class PedidoService(
    private val pedidoCounter: Counter,
    private val pedidoTimer: Timer,
    private val meterRegistry: MeterRegistry
) {
    suspend fun criarPedido(request: CriarPedidoRequest): Pedido {
        return pedidoTimer.recordSuspend {
            try {
                val pedido = processarInterno(request)
                pedidoCounter.increment()
                meterRegistry.counter("pedidos.status",
                    "status", "sucesso"
                ).increment()
                pedido
            } catch (e: Exception) {
                meterRegistry.counter("pedidos.status",
                    "status", "erro",
                    "tipo_erro", e.javaClass.simpleName
                ).increment()
                throw e
            }
        }
    }
}

// Extension function para medir suspend functions
suspend fun <T> Timer.recordSuspend(block: suspend () -> T): T {
    val sample = Timer.start()
    try {
        return block()
    } finally {
        sample.stop(this)
    }
}
```

### Métricas de Negocios

Alem de métricas tecnicas, monitore métricas de negócio:

```kotlin
@Component
class BusinessMetrics(private val meterRegistry: MeterRegistry) {

    fun registrarVenda(valor: Double, categoria: String) {
        meterRegistry.counter("vendas.total",
            "categoria", categoria
        ).increment()

        meterRegistry.summary("vendas.valor",
            "categoria", categoria
        ).record(valor)
    }

    fun registrarCarrinhoAbandonado(valor: Double) {
        meterRegistry.counter("carrinho.abandonado").increment()
        meterRegistry.summary("carrinho.abandonado.valor").record(valor)
    }

    // Gauge para valores instantaneos
    fun monitorarFilaProcessamento(fila: Queue<*>) {
        meterRegistry.gauge("fila.processamento.tamanho", fila) {
            it.size.toDouble()
        }
    }
}
```

### 3. Tracing Distribuído

Em arquiteturas de microsserviços, tracing permite acompanhar uma requisicao através de múltiplos serviços:

```kotlin
// OpenTelemetry com Spring Boot e Kotlin
@RestController
@RequestMapping("/api/pedidos")
class PedidoController(
    private val pedidoService: PedidoService,
    private val tracer: Tracer
) {
    @PostMapping
    suspend fun criarPedido(
        @RequestBody request: CriarPedidoRequest
    ): ResponseEntity<PedidoDTO> {
        val span = tracer.spanBuilder("criar-pedido")
            .setAttribute("pedido.cliente_id", request.clienteId)
            .setAttribute("pedido.itens_count", request.itens.size.toLong())
            .startSpan()

        return try {
            span.makeCurrent().use {
                val pedido = pedidoService.criarPedido(request)
                span.setAttribute("pedido.id", pedido.id)
                span.setAttribute("pedido.valor", pedido.valor)
                ResponseEntity.status(HttpStatus.CREATED).body(pedido.toDTO())
            }
        } catch (e: Exception) {
            span.recordException(e)
            span.setStatus(StatusCode.ERROR, e.message ?: "Erro desconhecido")
            throw e
        } finally {
            span.end()
        }
    }
}
```

### Propagacao de Contexto com Coroutines

Um desafio específico de Kotlin e a propagacao de contexto de tracing com coroutines:

```kotlin
// Propagacao de contexto OpenTelemetry com coroutines
import io.opentelemetry.extension.kotlin.asContextElement

suspend fun processarComTracing(pedidoId: String) {
    val span = tracer.spanBuilder("processar-pagamento")
        .startSpan()

    withContext(span.asContextElement()) {
        try {
            // O contexto de tracing e propagado automaticamente
            val pagamento = pagamentoService.processar(pedidoId)
            notificacaoService.enviar(pedidoId, pagamento)
        } finally {
            span.end()
        }
    }
}
```

## Stack de Observabilidade

### Prometheus + Grafana

A combinacao mais popular para métricas:

```kotlin
// Exposicao de métricas para Prometheus
// application.yml
// management:
//   endpoints:
//     web:
//       exposure:
//         include: health,info,prometheus
//   metrics:
//     export:
//       prometheus:
//         enabled: true
//     tags:
//       application: pedido-service
//       environment: production

// Métricas customizadas expostas automaticamente
@Component
class CustomMetrics(private val registry: MeterRegistry) {

    init {
        // Metrica de uptime
        registry.gauge("app.uptime.seconds", this) {
            ManagementFactory.getRuntimeMXBean().uptime / 1000.0
        }
    }
}
```

### ELK Stack (Elasticsearch, Logstash, Kibana)

Para logs centralizados, o ELK stack e o padrão da industria. Com Logback configurado para JSON e Filebeat para coleta, os logs de todas as instancias da sua aplicação ficam pesquisaveis em um único lugar.

### Jaeger ou Zipkin

Para tracing distribuído, Jaeger e Zipkin são as opções mais populares. OpenTelemetry permite trocar entre eles sem mudar o código da aplicação.

## Alertas Inteligentes

Observabilidade sem alertas e como ter cameras de segurança que ninguem assiste:

```kotlin
// Exemplo de cenarios que devem gerar alertas:
// 1. Taxa de erro acima de 5% por mais de 5 minutos
// 2. Latencia p99 acima de 2 segundos
// 3. Uso de memoria acima de 90%
// 4. Fila de processamento crescendo continuamente

// Health check detalhado para alertas
@Component
class DetailedHealthIndicator(
    private val dataSource: DataSource,
    private val redisTemplate: RedisTemplate<String, String>
) : HealthIndicator {

    override fun health(): Health {
        val checks = mutableMapOf<String, Any>()

        // Verificar banco de dados
        val dbHealthy = try {
            dataSource.connection.use { it.isValid(3) }
        } catch (e: Exception) {
            false
        }
        checks["database"] = if (dbHealthy) "UP" else "DOWN"

        // Verificar Redis
        val redisHealthy = try {
            redisTemplate.connectionFactory?.connection?.ping() != null
        } catch (e: Exception) {
            false
        }
        checks["redis"] = if (redisHealthy) "UP" else "DOWN"

        return if (dbHealthy && redisHealthy) {
            Health.up().withDetails(checks).build()
        } else {
            Health.down().withDetails(checks).build()
        }
    }
}
```

## Conclusão

Observabilidade não e luxo, e necessidade. Em aplicações Kotlin de producao, ter logs estruturados, métricas abrangentes e tracing distribuído e o que separa equipes que reagem a problemas de equipes que os previnem.

Comece com logs estruturados (e o mais rápido de implementar), adicione métricas com Micrometer e Prometheus, e por fim implemente tracing distribuído com OpenTelemetry. Cada camada adiciona visibilidade sobre o comportamento da sua aplicação e reduz o tempo de resolução de problemas. Muitas das melhores ferramentas de observabilidade — como Prometheus, Grafana e Jaeger — são escritas em <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a>, o que torna essa linguagem um excelente complemento para quem trabalha com infraestrutura. Invista em observabilidade e durma mais tranquilo sabendo que sua aplicação esta sendo monitorada.
