Timber é uma biblioteca de logging leve para Android com arquitetura extensível baseada em árvores (Tree), substituindo o android.util.Log padrão em milhares de projetos. De acordo com GitHub, 2024, a biblioteca acumulou mais de 10.000 estrelas e é usada em aplicativos com mais de 1 bilhão de instalações. Timber resolve três problemas principais da Log API: falta de tag automática, verificação obrigatória isLoggable e natureza estática das chamadas.
Pontos Principais
Timber é uma biblioteca de código aberto para Android criada por Jake Wharton em 2013 como alternativa ao android.util.Log padrão. A ideia principal do Timber é substituir a API Log estática com tag manual obrigatória por um mecanismo automático que determina a origem da chamada através da pilha.
A biblioteca é construída sobre o padrão arquitetural Composite com Árvores (Tree). Em vez de uma única classe Log com comportamento fixo, Timber gerencia uma "floresta" de árvores — cada árvore é responsável por seu próprio canal de saída: console, arquivo, Crashlytics, servidor remoto. O desenvolvedor pode adicionar qualquer número de árvores e combiná-las.
De acordo com Google I/O 2019, Timber é recomendado pelo Google como uma prática recomendada para logging em aplicações Android. A biblioteca ocupa menos de 10 KB no APK e não tem dependências externas, tornando-a uma escolha ideal para projetos de qualquer escala.
Timber resolve o problema de tags inconsistentes em grandes equipes. Quando cada desenvolvedor escreve tags manualmente, erros de digitação e discrepâncias são inevitáveis — uma classe é registrada como "MainActivity", outra como "MAIN_ACTIVITY". Timber deriva automaticamente a tag do nome da classe: MainActivity.kt → tag MainActivity.
Arquitetura do Timber consiste em dois componentes: a classe estática central Timber e a classe abstrata Timber.Tree. Timber atua como uma fachada que delega cada chamada de log a todas as árvores plantadas. Cada árvore decide se deve processar a mensagem e, se sim, para onde enviá-la.
DebugTree é a implementação padrão de Tree incluída na biblioteca. Ela determina a tag analisando a pilha de chamadas: sobe 8 quadros a partir do ponto de chamada Timber.d() e encontra o nome da classe que invocou o método de log. O DebugTree se desativa automaticamente (não produz saída) em builds release porque verifica BuildConfig.DEBUG.
Forest (Floresta) — a coleção de todas as árvores plantadas. Quando o método Timber.d("mensagem") é chamado, a biblioteca passa iterativamente a mensagem para todas as árvores na ordem em que foram plantadas. Cada árvore pode filtrar a mensagem por nível, tag ou conteúdo, e processá-la à sua maneira.
A ordem de plantio importa: a primeira árvore plantada é processada primeiro. É recomendado plantar DebugTree por último, para que as árvores personalizadas (por exemplo, Crashlytics) processem a mensagem antes que ela chegue ao Logcat.
Timber é thread-safe — todos os métodos são sincronizados através de um bloqueio interno. Isso garante que mensagens de diferentes threads não se misturem. No entanto, dentro de uma árvore personalizada, a sincronização é responsabilidade do desenvolvedor: se a árvore escreve em um arquivo, synchronized ou ReentrantLock devem ser usados.
// Inicialização da floresta de árvores em Application.onCreate
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
Timber.plant(CrashReportingTree())
Timber.plant(FileLoggingTree())
Timber.i("Timber planted with 3 trees")
}
}
Instalação do Timber é feita adicionando uma única dependência ao build.gradle. A biblioteca está publicada no Maven Central sob o artefato com.jakewharton.timber:timber. A versão atual em 2024 é 5.0.1, a última atualização estável.
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
Configuração mínima após a instalação — plantar DebugTree em Application.onCreate. Sem esta etapa, Timber ignorará todas as chamadas de log sem lançar exceções. Este é um comportamento padrão seguro: se nenhuma árvore for plantada, a biblioteca funciona inativa com sobrecarga mínima.
De acordo com Jake Wharton, 2023, 70% dos problemas do Timber para novos usuários estão relacionados à inicialização esquecida ou incorreta. Timber não gera um erro quando não há árvores — os desenvolvedores esperam que os logs apareçam no Logcat, mas nada acontece.
Para testes, Timber fornece Timber.asTree() — um método que retorna a árvore atual ou null. Isso é conveniente para testes unitários: você pode substituir a árvore por um mock e verificar se a mensagem de log foi enviada com o nível e tag corretos.
Árvore personalizada — a principal razão para usar Timber em vez da API Log padrão. Substituindo os métodos de Tree, você pode rotear logs de qualquer nível para Crashlytics, sistema de arquivos, Remote Config ou seu próprio servidor.
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// Apenas Error e WTF para crash-reporting
return priority >= Log.ERROR
}
override fun log(priority: Int, tag: String?,
message: String, t: Throwable?) {
if (t != null) {
FirebaseCrashlytics.getInstance()
.recordException(t)
} else {
FirebaseCrashlytics.getInstance()
.log("[$tag] $message")
}
}
}
Métodos para substituir: isLoggable(tag, priority) — um filtro que determina se a mensagem deve ser processada (a implementação base retorna true). log(priority, tag, message, t) — a lógica principal de processamento. prepareLog(priority, tag, throwable, message, args) — chamado antes da formatação, permite modificar a mensagem antes do processamento.
Uma vantagem importante das árvores personalizadas é sem reflexão. Ao contrário de muitos frameworks de logging, Timber não usa Reflection API para determinar a tag ou o nível. A tag é calculada analisando a pilha de chamadas (Throwable.stackTrace), que funciona ordens de magnitude mais rápido.
Comparação do Timber com a API Log padrão mostra quatro diferenças principais: tag automática, suporte a formatação de string com varargs, múltiplos canais de saída e comportamento seguro quando não inicializado.
| Parâmetro | android.util.Log | Timber |
|---|---|---|
| Detecção de tag | Manual, constante de string | Automática, via pilha de chamadas |
| Formatação | Concatenação ou String.format | Varargs embutidos + placeholder %s |
| Canais de saída | Apenas Logcat | Árvores: Logcat, arquivo, Crashlytics, etc. |
| Comportamento sem inicialização | Sempre funciona | Não produz saída |
| Desempenho | Nível base | Formatação preguiçosa via isLoggable |
Principal argumento contra Timber — dependência de uma biblioteca de terceiros. Para um projeto simples com logging mínimo, usar Timber pode ser excessivo. No entanto, de acordo com Google Play Console, 2024, mais de 60% dos 1000 principais aplicativos no Google Play usam Timber, confirmando sua confiabilidade e eficiência.
O desempenho do Timber em builds release é comparável ao da API Log padrão. Quando nenhuma árvore é plantada, o método Timber.d() verifica a presença de árvores (um if) e retorna — sem formatação de string. Isso é mais rápido que Log.d() com concatenação, que sempre executa.
Primeira regra — sempre verifique a inicialização do Timber nos testes. Use Timber.asTree() para verificar se uma árvore está plantada. Em testes unitários, plante TestTree que salva mensagens em uma lista para verificações assert.
Segunda regra — não misture Timber e android.util.Log no mesmo projeto. Se o projeto já usa Timber, todas as novas chamadas de log devem passar por ele. A mistura leva a mensagens duplicadas e confusão durante a análise.
Terceira regra — plante CrashReportingTree sem verificar BuildConfig.DEBUG. Ao contrário do DebugTree, a árvore de crash deve funcionar tanto em debug quanto em release — isso garante que os erros de teste também sejam capturados pelo sistema de relatório de crash.
Quarta regra — use os níveis embutidos do Timber: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf(). Evite chamar Timber.log() diretamente com prioridade numérica — isso reduz a legibilidade do código e complica a refatoração.
Quinta regra — para bibliotecas e módulos, use Timber.tag("CustomTag"). Este método retorna uma árvore temporária com uma tag substituída sem afetar a configuração global. Isso permite o registro a partir do código da biblioteca com um identificador personalizado.
Perguntas Frequentes
Sim — Timber é seguro de usar em bibliotecas. Se nenhuma árvore for plantada no aplicativo, as chamadas Timber não causam erros. Para bibliotecas, é recomendado usar Timber.tag("LibraryTag") para identificar a origem do log.
Através da pilha de chamadas (stack trace) — o DebugTree sobe 8 quadros a partir do ponto de chamada Timber.d() e extrai o nome da classe. O método Throwable.stackTrace é usado para determinar a classe chamadora sem custos de Reflection API.
Logcat é um utilitário do sistema Android para visualizar logs. Timber é uma biblioteca para escrever logs. Timber envia mensagens para o Logcat através do DebugTree, mas também pode enviá-las para arquivos, Crashlytics, Sentry e outros canais através de árvores personalizadas.
Não — Timber está vinculado ao Android SDK (android.util.Log). Para projetos KMP, considere Kermit ou Napier — bibliotecas de logging multiplataforma com arquitetura de árvore semelhante, funcionando em Android, iOS, JVM e JS.
Use Timber.uprootAll() — o método remove todas as árvores registradas. Timber.uproot(tree) remove uma árvore específica. Isso é útil em testes para redefinir o estado entre métodos de teste.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também