Logging em aplicações Kotlin: melhores práticas
Logging não é apenas registrar eventos, mas uma ferramenta chave para depuração, monitoramento e análise do funcionamento da aplicação. No ecossistema Kotlin, graças à compatibilidade total com Java, todas as bibliotecas populares como SLF4J e Logback estão disponíveis. No entanto, Kotlin oferece suas próprias abordagens idiomáticas que tornam o código mais limpo e seguro. Neste artigo, vamos abordar as melhores práticas de logging em Kotlin: desde a escolha da biblioteca até a configuração de logs estruturados para produção.
1. Escolha da biblioteca para logging em Kotlin
O stack mais popular para aplicações Kotlin é SLF4J (fachada) + Logback (implementação). No entanto, para Kotlin, recomenda-se usar o wrapper kotlin-logging, que adiciona funções inline e evita sobrecargas desnecessárias.
// build.gradle.ktsdependencies { implementation("io.github.microutils:kotlin-logging:3.0.5") implementation("ch.qos.logback:logback-classic:1.4.14")}Vantagens do kotlin-logging:
- Inicialização preguiçosa do logger — o logger é criado apenas no primeiro acesso.
- Funções inline — a verificação do nível de logging ocorre em tempo de compilação, reduzindo o overhead.
- Suporte para funções suspend — funcionamento correto em corrotinas.
Exemplo de declaração do logger:
import mu.KotlinLogging
private val logger = KotlinLogging.logger {}2. Níveis corretos de logging
Usar o nível correto é a base para logs legíveis. Em Kotlin, assim como em Java, estão disponíveis 5 níveis: TRACE, DEBUG, INFO, WARN, ERROR.
Recomendações:
- TRACE — apenas para depuração detalhada (ex.: entrada/saída de método).
- DEBUG — informação para o desenvolvedor (valores de variáveis, estado de objetos).
- INFO — eventos de negócio (requisição processada, usuário autenticado).
- WARN — situações anormais que não quebram a aplicação (nova tentativa de conexão).
- ERROR — exceções e falhas que exigem atenção.
Exemplo com diferentes níveis:
logger.debug { "Processando requisição: $requestId" }logger.info { "Usuário ${user.name} registrado com sucesso" }logger.warn { "Pool de conexões 90% cheio, tamanho atual: $poolSize" }logger.error(ex) { "Erro ao salvar pedido ${orderId}" }Importante: Em Kotlin, para passar uma exceção, use logger.error(throwable) { "mensagem" }, em vez de logger.error("mensagem", throwable) — isso é mais idiomático.
3. Logging estruturado (JSON)
Em arquiteturas modernas de microsserviços, logs textuais são inconvenientes para análise. É melhor usar logs estruturados no formato JSON. Isso permite filtrar logs facilmente no ELK, Grafana ou Splunk.
Configuração do Logback para logs JSON:
// logback.xml<configuration> <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="net.logstash.logback.encoder.LogstashEncoder"/> </appender>
<root level="INFO"> <appender-ref ref="JSON"/> </root></configuration>Para adicionar campos personalizados, use MDC (Mapped Diagnostic Context):
import org.slf4j.MDC
fun processOrder(orderId: String) { MDC.put("orderId", orderId) MDC.put("userId", currentUserId()) logger.info { "Início do processamento do pedido" } // ... lógica MDC.clear()}