Timber — egy könnyű naplózási könyvtár Androidhoz, kiterjeszthető, fákon (Tree) alapuló architektúrával, amely ezernyi projektben váltotta fel a szabványos android.util.Log-ot. A GitHub, 2024 adatai szerint a könyvtár több mint 10 000 csillagot gyűjtött, és több mint 1 milliárd telepítéssel rendelkező alkalmazásokban használják. A Timber a Log API három fő problémáját oldja meg: az automatikus tag hiányát, a kötelező isLoggable ellenőrzést és a hívások statikus természetét.
Főbb pontok
Timber — egy nyílt forráskódú könyvtár Androidhoz, amelyet Jake Wharton hozott létre 2013-ban a szabványos android.util.Log alternatívájaként. A Timber kulcsgondolata — a statikus Log API lecserélése kötelező kézi tag-ről automatikus mechanizmusra, amely a hívás forrását a verem segítségével határozza meg.
A könyvtár a Composite fákkal (Tree) architekturális mintára épül. Egyetlen, rögzített viselkedésű Log osztály helyett a Timber egy „erdőt“ kezel a fákból — minden fa a saját kimeneti csatornájáért felelős: konzol, fájl, Crashlytics, távoli szerver. A fejlesztő tetszőleges számú fát adhat hozzá és kombinálhatja őket.
A Google I/O 2019 adatai szerint a Google a Timber-t ajánlja legjobb gyakorlatként a naplózáshoz Android alkalmazásokban. A könyvtár kevesebb mint 10 KB-ot foglal az APK-ban, és nincsenek külső függőségei, ami ideális választássá teszi bármilyen méretű projektek számára.
A Timber megoldja a következetlen tag-ek problémáját nagy csapatokban. Amikor minden fejlesztő kézzel írja a tag-et, a gépelési hibák és eltérések elkerülhetetlenek — az egyik osztály „MainActivity”-ként, a másik „MAIN_ACTIVITY”-ként naplózódik. A Timber automatikusan kinyeri a tag-et az osztály nevéből:MainActivity.kt → tag MainActivity.
A Timber architektúrája két komponensből áll: a központi statikus Timber osztályból és az absztrakt Timber.Tree osztályból. A Timber homlokzatként működik, amely minden naplóhívást delegál az összes ültetett (planted) fához. Minden fa eldönti, hogy fel kell-e dolgoznia az üzenetet, és ha igen — hová irányítsa.
DebugTree — a könyvtárral szállított szabványos Tree implementáció. A tag-et a hívási verem elemzésével határozza meg: 8 kerettel feljebb lép a Timber.d() hívási pontjától, és megkeresi a naplózási metódust meghívó osztály nevét. A DebugTree automatikusan kikapcsol (semmit sem ír ki) release verziókban, mert ellenőrzi a BuildConfig.DEBUG-ot.
Forest (erdő) — az összes ültetett fa gyűjteménye. Amikor a Timber.d(„üzenet”) metódus meghívódik, a könyvtár iteratívan továbbítja az üzenetet az összes fának az ültetés sorrendjében. Minden fa szűrheti az üzenetet szint, tag vagy tartalom szerint, és a saját módján dolgozhatja fel.
Az ültetés sorrendje fontos: az elsőként ültetett fa dolgozódik fel először. Javasolt a DebugTree-t utolsóként ültetni, hogy az egyedi fák (pl. Crashlytics) feldolgozzák az üzenetet, mielőtt az a Logcat-be kerül.
A Timber szálbiztos — minden metódus szinkronizálva van egy belső lock segítségével. Ez garantálja, hogy a különböző szálakból érkező üzenetek nem keverednek össze. Az egyedi fán belül azonban a szinkronizáció a fejlesztő felelőssége: ha a fa fájlba ír, synchronized vagy ReentrantLock használata szükséges.
// Fák erdejének inicializálása az Application.onCreate-ben
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")
}
}
A Timber telepítése egyetlen függőség hozzáadásával történik a build.gradle-ben. A könyvtár a Maven Central-ben jelent meg a com.jakewharton.timber:timber artifact alatt. Aktuális verzió 2024-re — 5.0.1, az utolsó stabil frissítés.
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
Minimális beállítás telepítés után — a DebugTree ültetése az Application.onCreate-ben. E lépés nélkül a Timber figyelmen kívül hagyja az összes naplóhívást, kivételek dobása nélkül. Ez egy biztonságos alapértelmezett viselkedés: ha a fa nincs ültetve, a könyvtár minimális overhead-del üresen működik.
Jake Wharton, 2023 adatai szerint az új felhasználók Timber-rel kapcsolatos problémáinak 70%-a az elfelejtett vagy helytelen inicializáláshoz kapcsolódik. A Timber nem generál hibát a fák hiányában — a fejlesztők azt várják, hogy a naplók megjelennek a Logcat-ben, de semmi sem történik.
Teszteléshez a Timber a Timber.asTree() metódust biztosítja — amely visszaadja az aktuális fát vagy null-t. Ez kényelmes az egységtesztekben történő ellenőrzéshez: a fa helyettesíthető egy mock-kal, és ellenőrizhető, hogy a naplóüzenet a megfelelő szinttel és tag-gel lett elküldve.
Egyedi fa — a fő ok a Timber használatára a szabványos Log API helyett. A Tree metódusainak felülírásával bármely szintű napló irányítható a Crashlytics-be, fájlrendszerbe, Remote Config-ba vagy saját szerverre.
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// Csak Error és WTF a crash-reportinghoz
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")
}
}
}
Felülírható metódusok: isLoggable(tag, priority) — szűrő, amely meghatározza, hogy az üzenetet fel kell-e dolgozni (az alap implementáció true-t ad vissza). log(priority, tag, message, t) — a feldolgozás fő logikája. prepareLog(priority, tag, throwable, message, args) — a formázás előtt hívódik meg, lehetővé teszi az üzenet módosítását a feldolgozás előtt.
Az egyedi fák fontos előnye — a reflexió hiánya. Sok naplózási keretrendszerrel ellentétben a Timber nem használ Reflection API-t a tag vagy a szint meghatározásához. A tag a hívási verem elemzésével (Throwable.stackTrace) számítható ki, ami nagyságrenddel gyorsabban működik.
A Timber és a szabványos Log API összehasonlítása négy kulcsfontosságú különbséget mutat: automatikus tag, string formázás támogatása varargs-szal, több kimeneti csatorna lehetősége és biztonságos viselkedés inicializálás hiányában.
| Paraméter | android.util.Log | Timber |
|---|---|---|
| Tag meghatározása | Kézi, string konstans | Automatikus, hívási verem alapján |
| Formázás | Összefűzés vagy String.format | Beépített varargs + %s helyőrző |
| Kimeneti csatornák | Csak Logcat | Fák: Logcat, fájl, Crashlytics stb. |
| Viselkedés inicializálás nélkül | Mindig működik | Semmit sem ír ki |
| Teljesítmény | Alap szint | Lusta formázás isLoggable segítségével |
Fő érv a Timber ellen — függőség egy harmadik féltől származó könyvtártól. Egy egyszerű, minimális naplózású projekt esetén a Timber használata túlzás lehet. Azonban a Google Play Console, 2024 adatai szerint a Google Play top-1000 alkalmazásának több mint 60%-a használja a Timber-t, ami megerősíti megbízhatóságát és hatékonyságát.
A Timber teljesítménye release verziókban nem marad el a szabványos Log API-tól. Ültetett fák hiányában a Timber.d() metódus ellenőrzi a fák jelenlétét (egy if) és visszatér — string formázás nélkül. Ez gyorsabb, mint a Log.d() összefűzéssel, ami mindig végrehajtódik.
Első szabály — mindig ellenőrizze a Timber inicializálását a tesztekben. Használja a Timber.asTree() metódust annak ellenőrzésére, hogy a fa ültetve van-e. Az egységtesztekben ültessen TestTree-t, amely a listában tárolja az üzeneteket az assert ellenőrzésekhez.
Második szabály — ne keverje a Timber-t és az android.util.Log-ot egy projektben. Ha a projekt már használja a Timber-t, minden új naplóhívásnak azon keresztül kell mennie. A keverés az üzenetek duplikálódásához és zavarhoz vezet az elemzés során.
Harmadik szabály — ültesse a CrashReportingTree-t BuildConfig.DEBUG ellenőrzés nélkül. A DebugTree-től eltérően a crash fának mind debug, mind release módban működnie kell — ez garantálja, hogy a tesztelési hibák is bekerülnek a crash-reporting rendszerbe.
Negyedik szabály — használja a Timber beépített szintjeit: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf(). Kerülje a Timber.log() közvetlen hívását numerikus priority-vel — ez csökkenti a kód olvashatóságát és megnehezíti a refaktorálást.
Ötödik szabály — könyvtárakhoz és modulokhoz használja a Timber.tag(„CustomTag“) metódust. Ez a metódus egy ideiglenes fát ad vissza felülírt tag-gel, anélkül hogy befolyásolná a globális konfigurációt. Lehetővé teszi a naplózást könyvtári kódból egyedi azonosítóval.
Gyakran Ismételt Kérdések
Igen — a Timber biztonságosan használható könyvtárakban. Ha a fa nincs ültetve az alkalmazásban, a Timber hívások nem okoznak hibákat. Könyvtárak esetén a Timber.tag(„LibraryTag“) használata javasolt a naplók forrásának azonosításához.
A hívási veremen (stack trace) keresztül — a DebugTree 8 kerettel feljebb lép a Timber.d() hívási pontjától, és kinyeri az osztály nevét. A Throwable.stackTrace metódus a hívó osztály meghatározására szolgál a Reflection API költségei nélkül.
Logcat — az Android rendszersegédprogramja a naplók megtekintéséhez. Timber — könyvtár a naplók írásához. A Timber a DebugTree-n keresztül írja ki az üzeneteket a Logcat-be, de elküldheti azokat fájlokba, Crashlytics-be, Sentry-be és más csatornákba egyedi fákon keresztül.
Nem — a Timber az Android SDK-hoz (android.util.Log) kötődik. KMP projektekhez fontolja meg a Kermit vagy a Napier használatát — ezek többplatformos naplózási könyvtárak hasonló fa architektúrával, amelyek Androidon, iOS-en, JVM-en és JS-en működnek.
Használja a Timber.uprootAll() metódust — ez eltávolítja az összes regisztrált fát. A Timber.uproot(tree) egy adott fát távolít el. Ez hasznos a tesztekben az állapot visszaállításához a tesztmetódusok között.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is