Timber — ett lättviktigt loggningsbibliotek för Android med en utbyggbar arkitektur baserad på träd (Tree), som har ersatt standard android.util.Log i tusentals projekt. Enligt data från GitHub, 2024 har biblioteket samlat över 10 000 stjärnor och används i appar med en publik på över 1 miljard installationer. Timber löser tre huvudproblem med Log API: avsaknad av automatisk tag, obligatorisk isLoggable-kontroll och den statiska naturen av anrop.
Huvudpunkter
Timber — är ett open-source-bibliotek för Android, skapat av Jake Wharton 2013 som ett alternativ till standard android.util.Log. Kärnidén med Timber — att ersätta det statiska Log API med obligatorisk manuell tag med en automatisk mekanism som bestämmer anropskällan via stacken.
Biblioteket är byggt på arkitekturmönstret Composite med träd (Tree). Istället för en enda Log-klass med fast beteende hanterar Timber en ”skog” av träd — varje träd ansvarar för sin egen utgångskanal: konsol, fil, Crashlytics, fjärrserver. Utvecklaren kan lägga till valfritt antal träd och kombinera dem.
Enligt data från Google I/O 2019 rekommenderas Timber av Google som bästa praxis för loggning i Android-appar. Biblioteket tar mindre än 10 KB i APK och har inga externa beroenden, vilket gör det till ett idealiskt val för projekt av alla storlekar.
Timber löser problemet med inkonsekventa taggar i stora team. När varje utvecklare skriver taggen manuellt är stavfel och skillnader oundvikliga — en klass loggas som ”MainActivity”, en annan som ”MAIN_ACTIVITY”. Timber extraherar automatiskt taggen från klassnamnet:MainActivity.kt → tag MainActivity.
Timber-arkitekturen består av två komponenter: den centrala statiska klassen Timber och den abstrakta klassen Timber.Tree. Timber fungerar som en fasad som delegerar varje logganrop till alla planterade (planted) träd. Varje träd avgör om meddelandet ska behandlas, och om ja — vart det ska skickas.
DebugTree — standard Tree-implementeringen som medföljer biblioteket. Den bestämmer taggen genom analys av anropsstacken: stiger 8 ramar upp från anropspunkten för Timber.d() och hittar klassnamnet som anropade loggmetoden. DebugTree inaktiveras automatiskt (skriver ingenting) i release-byggen eftersom den kontrollerar BuildConfig.DEBUG.
Forest (skog) — samlingen av alla planterade träd. När metoden Timber.d(”meddelande”) anropas, skickar biblioteket iterativt meddelandet till alla träd i planteringsordning. Varje träd kan filtrera meddelandet efter nivå, tagg eller innehåll och behandla det på sitt eget sätt.
Planteringsordningen är viktig: det första planterade trädet behandlas först. Det rekommenderas att plantera DebugTree sist, så att anpassade träd (t.ex. Crashlytics) behandlar meddelandet innan det når Logcat.
Timber är trådsäkert — alla metoder synkroniseras via ett internt lås. Detta garanterar att meddelanden från olika trådar inte blandas ihop. Inuti ett anpassat träd ligger dock synkronisering på utvecklarens ansvar: om trädet skriver till en fil måste synchronized eller ReentrantLock användas.
// Initialisering av trädskogen i 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")
}
}
Installation av Timber görs genom att lägga till ett enda beroende i build.gradle. Biblioteket är publicerat i Maven Central under artefakten com.jakewharton.timber:timber. Aktuell version för 2024 — 5.0.1, senaste stabila uppdateringen.
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
Minsta konfiguration efter installation — plantera DebugTree i Application.onCreate. Utan detta steg ignorerar Timber alla logganrop, utan att kasta undantag. Detta är ett säkert standardbeteende: om trädet inte är planterat arbetar biblioteket på tomgång med minimal overhead.
Enligt data från Jake Wharton, 2023 är 70% av problemen med Timber hos nya användare relaterade till glömd eller felaktig initialisering. Timber genererar inget fel när träd saknas — utvecklare förväntar sig att loggar ska visas i Logcat, men ingenting händer.
För testning tillhandahåller Timber Timber.asTree() — en metod som returnerar det aktuella trädet eller null. Detta är praktiskt för kontroll i enhetstester: trädet kan ersättas med en mock och kontrollera att loggmeddelandet skickades med rätt nivå och tagg.
Anpassat träd — den främsta anledningen att använda Timber istället för standard Log API. Genom att åsidosätta Tree-metoder kan loggar på valfri nivå dirigeras till Crashlytics, filsystem, Remote Config eller egen server.
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// Endast Error och WTF för 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")
}
}
}
Metoder att åsidosätta: isLoggable(tag, priority) — filter som avgör om meddelandet ska behandlas (grundimplementeringen returnerar true). log(priority, tag, message, t) — huvudlogik för behandling. prepareLog(priority, tag, throwable, message, args) — anropas före formatering, gör det möjligt att ändra meddelandet före behandling.
En viktig fördel med anpassade träd — ingen reflektion. Till skillnad från många loggningsramverk använder Timber inte Reflection API för att bestämma tagg eller nivå. Taggen beräknas genom analys av anropsstacken (Throwable.stackTrace), vilket fungerar en storleksordning snabbare.
Jämförelse av Timber med standard Log API visar fyra viktiga skillnader: automatisk tagg, stöd för strängformatering med varargs, möjlighet till flera utgångskanaler och säkert beteende vid avsaknad av initialisering.
| Parameter | android.util.Log | Timber |
|---|---|---|
| Bestämning av tagg | Manuell, strängkonstant | Automatisk, via anropsstack |
| Formatering | Konkatenering eller String.format | Inbyggd varargs + platshållare %s |
| Utgångskanaler | Endast Logcat | Träd: Logcat, fil, Crashlytics m.fl. |
| Beteende utan initialisering | Fungerar alltid | Skriver ingenting |
| Prestanda | Grundnivå | Lat formatering via isLoggable |
Främsta argumentet mot Timber — beroende av ett tredjepartsbibliotek. För ett enkelt projekt med minimal loggning kan användning av Timber vara överdriven. Men enligt data från Google Play Console, 2024 använder över 60% av topp-1000-apparna i Google Play Timber, vilket bekräftar dess tillförlitlighet och effektivitet.
Prestandan för Timber i release-byggen är inte sämre än standard Log API. När det inte finns några planterade träd kontrollerar Timber.d() förekomsten av träd (en if) och återvänder — utan strängformatering. Detta är snabbare än Log.d() med konkatenering, som alltid utförs.
Första regeln — kontrollera alltid Timber-initialiseringen i tester. Använd Timber.asTree() för att verifiera att trädet är planterat. I enhetstester, plantera TestTree som sparar meddelanden i en lista för assert-kontroller.
Andra regeln — blanda inte Timber och android.util.Log i samma projekt. Om projektet redan använder Timber måste alla nya logganrop gå genom det. Blandning leder till dubblerade meddelanden och förvirring vid analys.
Tredje regeln — plantera CrashReportingTree utan BuildConfig.DEBUG-kontroll. Till skillnad från DebugTree måste crash-trädet fungera både i debug och release — detta garanterar att testfel också kommer in i crash-reporting-systemet.
Fjärde regeln — använd Timbers inbyggda nivåer: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf(). Undvik direktanrop av Timber.log() med numerisk priority — detta minskar kodens läsbarhet och försvårar refaktorering.
Femte regeln — för bibliotek och moduler, använd Timber.tag(”CustomTag”). Denna metod returnerar ett temporärt träd med åsidosatt tagg, utan att påverka den globala konfigurationen. Detta möjliggör loggning från bibliotekskod med anpassad identifierare.
Vanliga frågor
Ja — Timber är säkert att använda i bibliotek. Om trädet inte är planterat i appen orsakar Timber-anrop inga fel. För bibliotek rekommenderas att använda Timber.tag(”LibraryTag”) för att identifiera loggkällan.
Via anropsstacken (stack trace) — DebugTree stiger 8 ramar upp från anropspunkten för Timber.d() och extraherar klassnamnet. Metoden Throwable.stackTrace används för att bestämma den anropande klassen utan kostnader för Reflection API.
Logcat — systemverktyget i Android för att visa loggar. Timber — ett bibliotek för att skriva loggar. Timber skriver ut meddelanden till Logcat via DebugTree, men kan också skicka dem till filer, Crashlytics, Sentry och andra kanaler via anpassade träd.
Nej — Timber är bundet till Android SDK (android.util.Log). För KMP-projekt, överväg Kermit eller Napier — plattformsoberoende loggningsbibliotek med liknande trädarkitektur som fungerar på Android, iOS, JVM och JS.
Använd Timber.uprootAll() — denna metod tar bort alla registrerade träd. Timber.uproot(tree) tar bort ett specifikt träd. Detta är användbart i tester för att återställa tillståndet mellan testmetoder.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också