Timber — o bibliotecă ușoară de logare pentru Android cu arhitectură extensibilă bazată pe arbori (Tree), care a înlocuit android.util.Log standard în mii de proiecte. Conform datelor GitHub, 2024, biblioteca a adunat peste 10 000 de stele și este folosită în aplicații cu o audiență de peste 1 miliard de instalări. Timber rezolvă trei probleme principale ale Log API: lipsa tag-ului automat, verificarea obligatorie isLoggable și natura statică a apelurilor.
Principalele puncte
Timber — este o bibliotecă open-source pentru Android, creată de Jake Wharton în 2013 ca alternativă la android.util.Log standard. Ideea cheie a Timber — înlocuirea API-ului static Log cu tag manual obligatoriu cu un mecanism automat care determină sursa apelului prin stivă.
Biblioteca este construită pe modelul arhitectural Composite cu arbori (Tree). În locul unei singure clase Log cu comportament fix, Timber gestionează o „pădure“ de arbori — fiecare arbore răspunde pentru canalul său de ieșire: consolă, fișier, Crashlytics, server de la distanță. Dezvoltatorul poate adăuga orice număr de arbori și le poate combina.
Conform datelor Google I/O 2019, Timber este recomandat de Google ca cea mai bună practică pentru logare în aplicațiile Android. Biblioteca ocupă mai puțin de 10 KB în APK și nu are dependențe externe, ceea ce o face alegerea ideală pentru proiecte de orice scară.
Timber rezolvă problema tag-urilor inconsecvente în echipele mari. Când fiecare dezvoltator scrie tag-ul manual, greșelile de tipar și discrepanțele sunt inevitabile — o clasă se loghează ca „MainActivity“, alta ca „MAIN_ACTIVITY“. Timber extrage automat tag-ul din numele clasei:MainActivity.kt → tag MainActivity.
Arhitectura Timber constă din două componente: clasa statică centrală Timber și clasa abstractă Timber.Tree. Timber acționează ca o fațadă care delegă fiecare apel de log tuturor arborilor plantați (planted). Fiecare arbore decide dacă trebuie să proceseze mesajul și dacă da — încotro să îl direcționeze.
DebugTree — implementarea standard Tree furnizată cu biblioteca. Aceasta determină tag-ul prin analiza stivei de apeluri: se ridică cu 8 cadre de la punctul de apel Timber.d() și găsește numele clasei care a apelat metoda de log. DebugTree se dezactivează automat (nu scoate nimic) în versiunile release, deoarece verifică BuildConfig.DEBUG.
Forest (pădure) — colecția tuturor arborilor plantați. Când este apelată metoda Timber.d(„mesaj”), biblioteca transmite iterativ mesajul tuturor arborilor în ordinea plantării. Fiecare arbore poate filtra mesajul după nivel, tag sau conținut și îl poate procesa în felul său.
Ordinea plantării este importantă: primul arbore plantat este procesat primul. Se recomandă plantarea DebugTree ca ultimul, pentru ca arborii personalizați (de exemplu, Crashlytics) să proceseze mesajul înainte ca acesta să ajungă în Logcat.
Timber este sigur pentru fire de execuție — toate metodele sunt sincronizate printr-un blocaj intern. Aceasta garantează că mesajele din diferite fire nu se amestecă. Totuși, în interiorul unui arbore personalizat, sincronizarea cade în sarcina dezvoltatorului: dacă arborele scrie într-un fișier, trebuie să folosească synchronized sau ReentrantLock.
// Inițializarea pădurii de arbori în 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")
}
}
Instalarea Timber se face prin adăugarea unei singure dependențe în build.gradle. Biblioteca este publicată în Maven Central sub artifactul com.jakewharton.timber:timber. Versiunea actuală pentru 2024 — 5.0.1, ultima actualizare stabilă.
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
Configurarea minimă după instalare — plantarea DebugTree în Application.onCreate. Fără acest pas, Timber va ignora toate apelurile de log, fără a arunca excepții. Acesta este un comportament implicit sigur: dacă arborele nu este plantat, biblioteca funcționează în gol, cu un overhead minim.
Conform datelor Jake Wharton, 2023, 70% dintre problemele cu Timber la utilizatorii noi sunt legate de inițializarea uitată sau incorectă. Timber nu generează eroare la absența arborilor — dezvoltatorii se așteaptă ca logurile să apară în Logcat, dar nu se întâmplă nimic.
Pentru testare, Timber oferă Timber.asTree() — o metodă care returnează arborele curent sau null. Acest lucru este convenabil pentru verificare în testele unitare: se poate înlocui arborele cu un mock și verifica dacă mesajul de log a fost trimis cu nivelul și tag-ul corect.
Arborele personalizat — principalul motiv pentru a folosi Timber în locul Log API standard. Prin suprascrierea metodelor Tree se pot direcționa logurile de orice nivel către Crashlytics, sistemul de fișiere, Remote Config sau propriul server.
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// Doar Error și WTF pentru 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")
}
}
}
Metode de suprascris: isLoggable(tag, priority) — filtrul care determină dacă trebuie procesat mesajul (implementarea de bază returnează true). log(priority, tag, message, t) — logica principală de procesare. prepareLog(priority, tag, throwable, message, args) — se apelează înainte de formatare, permite modificarea mesajului înainte de procesare.
Un avantaj important al arborilor personalizați — absența reflecției. Spre deosebire de multe framework-uri de logare, Timber nu folosește Reflection API pentru determinarea tag-ului sau nivelului. Tag-ul se calculează prin analiza stivei de apeluri (Throwable.stackTrace), ceea ce funcționează cu un ordin de mărime mai rapid.
Comparația Timber cu Log API standard arată patru diferențe cheie: tag automat, suport pentru formatarea șirurilor cu varargs, posibilitatea mai multor canale de ieșire și comportament sigur la absența inițializării.
| Parametru | android.util.Log | Timber |
|---|---|---|
| Determinarea tag-ului | Manual, constantă string | Automat, după stiva de apeluri |
| Formatarea | Concatenare sau String.format | Varargs încorporat + placeholdere %s |
| Canale de ieșire | Doar Logcat | Arbori: Logcat, fișier, Crashlytics etc. |
| Comportament fără inițializare | Funcționează întotdeauna | Nu scoate nimic |
| Performanță | Nivel de bază | Formatare leneșă prin isLoggable |
Principalul argument împotriva Timber — dependența de o bibliotecă terță. Pentru un proiect simplu cu logare minimă, utilizarea Timber poate fi excesivă. Totuși, conform datelor Google Play Console, 2024, peste 60% din top-1000 aplicații în Google Play folosesc Timber, ceea ce confirmă fiabilitatea și eficiența sa.
Performanța Timber în versiunile release nu este inferioară Log API standard. La absența arborilor plantați, metoda Timber.d() verifică prezența arborilor (un if) și se întoarce — fără formatarea șirului. Aceasta este mai rapidă decât Log.d() cu concatenare, care se execută întotdeauna.
Prima regulă — verificați întotdeauna inițializarea Timber în teste. Folosiți Timber.asTree() pentru verificarea că arborele este plantat. În testele unitare, plantați TestTree care salvează mesajele într-o listă pentru verificări assert.
A doua regulă — nu amestecați Timber și android.util.Log în același proiect. Dacă proiectul folosește deja Timber, toate apelurile noi de log trebuie să treacă prin el. Amestecarea duce la duplicarea mesajelor și confuzie în analiză.
A treia regulă — plantați CrashReportingTree fără verificarea BuildConfig.DEBUG. Spre deosebire de DebugTree, arborele crash trebuie să funcționeze atât în debug, cât și în release — aceasta garantează că erorile de testare ajung și ele în sistemul de crash-reporting.
A patra regulă — folosiți nivelurile încorporate Timber: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf(). Evitați apelul direct Timber.log() cu priority numeric — aceasta reduce lizibilitatea codului și complică refactorizarea.
A cincea regulă — pentru biblioteci și module folosiți Timber.tag(„CustomTag“). Această metodă returnează un arbore temporar cu tag suprascris, fără a afecta configurația globală. Permite logarea din codul de bibliotecă cu un identificator personalizat.
Întrebări frecvente
Da — Timber este sigur de folosit în biblioteci. Dacă arborele nu este plantat în aplicație, apelurile Timber nu cauzează erori. Pentru biblioteci se recomandă utilizarea Timber.tag(„LibraryTag“) pentru identificarea sursei logurilor.
Prin stiva de apeluri (stack trace) — DebugTree se ridică cu 8 cadre de la punctul de apel Timber.d() și extrage numele clasei. Metoda Throwable.stackTrace este folosită pentru determinarea clasei apelante fără costurile Reflection API.
Logcat — utilitarul de sistem Android pentru vizualizarea logurilor. Timber — bibliotecă pentru scrierea logurilor. Timber scoate mesaje în Logcat prin DebugTree, dar poate și să le trimită în fișiere, Crashlytics, Sentry și alte canale prin arbori personalizați.
Nu — Timber este legat de Android SDK (android.util.Log). Pentru proiecte KMP, luați în considerare Kermit sau Napier — biblioteci de logare multi-platformă cu arhitectură similară de arbori, care funcționează pe Android, iOS, JVM și JS.
Folosiți Timber.uprootAll() — metoda elimină toți arborii înregistrați. Timber.uproot(tree) elimină un arbore specific. Acest lucru este util în teste pentru resetarea stării între metodele de test.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și