Logging em Kotlin: abordagem idiomática e comparação de métodos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que o logging em Kotlin é um problema (e como resolvê-lo)



Se você já programou em Java, conhece o ritual: no início de cada classe, você escreve private static final Logger logger = LoggerFactory.getLogger(MyClass.class);. Entediante, repetitivo, mas funciona. Ao migrar para Kotlin, você deseja algo mais elegante — e é aí que a coisa fica interessante.



No Stack Overflow, a pergunta "Idiomatic way of logging in Kotlin" acumulou milhares de visualizações e dezenas de discussões. Acontece que, mesmo no Kotlin moderno, não existe uma única maneira "correta" de fazer logging. Neste artigo, vamos analisar três abordagens principais, comparar seu desempenho e legibilidade, e escolher a forma idiomática de logging em Kotlin que servirá para 90% dos projetos.



O problema: Logger em Kotlin não é tão simples



Kotlin é uma linguagem que valoriza a concisão. Mas o SLF4J (a biblioteca de logging mais popular no mundo Java/Kotlin) exige a especificação explícita da classe para cada logger. Em Java, isso é resolvido com Lombok ou templates de IDE. Em Kotlin, com os recursos da própria linguagem.



Temos vários desafios pela frente:



  • Não duplicar o nome da classe (isso é fonte de erros ao copiar)
  • Não comprometer o desempenho (logging é uma operação frequente)
  • Manter a legibilidade e concisão do código
  • Suportar a inicialização preguiçosa do logger


Vamos ver quais soluções a comunidade Kotlin oferece.



Abordagem 1: "Bruta" — Companion Object



A maneira mais direta é declarar o logger no companion object da classe.



class UserService {    companion object {        private val logger = LoggerFactory.getLogger(UserService::class.java)    }

fun createUser(name: String) { logger.info("Creating user: $name") }}


Vantagens:

  • Simplicidade e clareza
  • Não requer dependências adicionais


Desvantagens:

  • Indicação manual da classe — pode haver erros
  • Muito código boilerplate
  • Não funciona para interfaces e classes abstratas


Esta abordagem é a base da qual todas as outras partem. Funciona, mas não é a forma idiomática de logging em Kotlin — há boilerplate demais.



Abordagem 2: Funções Extension — Estilo Kotlin



Kotlin permite estender classes com funções extension. Por que não criar uma função que retorna um logger para qualquer classe?



inline fun <reified T : Any> T.logger(): Logger {    return LoggerFactory.getLogger(T::class.java)}

class OrderService { private val logger = logger()

fun processOrder(id: String) { logger.debug("Processing order: $id") }}


Aqui usamos um parâmetro de tipo reified em uma função inline. Isso permite obter a classe em tempo de execução sem reflexão.



Vantagens:

  • Conciso: private val logger = logger()
  • Seguro: a classe é obtida automaticamente
  • A função inline não cria objetos desnecessários


Desvantagens:

  • O logger é inicializado imediatamente na criação do objeto, e não de forma preguiçosa
  • Não funciona para funções de nível superior (fora de uma classe)


Isso já está mais próximo de uma solução idiomática. Mas podemos melhorar ainda mais.



Abordagem 3: Propriedades Delegadas — Elegância do Kotlin



Kotlin suporta propriedades delegadas. Podemos criar um delegate que inicializa o logger de forma preguiçosa e o armazena em cache.



class LoggerDelegate<in T : Any> : ReadOnlyProperty<T, Logger> {    private var logger: Logger? = null

override fun getValue(thisRef: T, property: KPropert

Blogs

Book Recommendations