Timber — лагана библиотека за логирање за Android са проширивом архитектуром заснованом на дрвећима (Tree), која је заменила стандардни android.util.Log у хиљадама пројеката. Према подацима GitHub, 2024, библиотека је прикупила преко 10 000 звездица и користи се у апликацијама са публиком од преко 1 милијарде инсталација. Timber решава три главна проблема Log API-ја: одсуство аутоматског tag-а, обавезну проверу isLoggable и статичку природу позива.
Главно
Timber — је open-source библиотека за Android, коју је створио Џејк Вортон (Jake Wharton) 2013. године као алтернатива стандардном android.util.Log. Кључна идеја Timber-а — замена статичког Log API-ја са обавезним ручним tag-ом аутоматским механизмом који одређује извор позива преко стека.
Библиотека је изграђена на архитектонском обрасцу Composite са дрвећима (Tree) . Уместо једне Log класе са фиксним понашањем, Timber управља „шумом“ од дрвећа — свако дрво је одговорно за свој канал излаза: конзолу, датотеку, Crashlytics, удаљени сервер. Програмер може додати било који број дрвећа и комбиновати их.
Према подацима Google I/O 2019, Timber препоручује Google као најбоља пракса за логирање у Android апликацијама. Библиотека заузима мање од 10 KB у APK-у и нема спољних зависности, што је чини идеалним избором за пројекте било које величине.
Timber решава проблем неконзистентних tag-ова у великим тимовима. Када сваки програмер пише tag ручно, неминовне су грешке у куцању и разлике — једна класа се логира као „MainActivity“, друга као „MAIN_ACTIVITY“. Timber аутоматски извлачи tag из имена класе:MainActivity.kt → tag MainActivity.
Архитектура Timber-а се састоји од две компоненте: централне статичке класе Timber и апстрактне класе Timber.Tree. Timber делује као фасада која делегира сваки лог позив свим посађеним (planted) дрвећима. Свако дрво одлучује да ли треба да обради поруку, и ако да — куда да је усмери.
DebugTree — стандардна имплементација Tree-ја која се испоручује са библиотеком. Она одређује tag кроз анализу стека позива: подиже се 8 оквира изнад тачке позива Timber.d() и проналази име класе која је позвала лог метод. DebugTree се аутоматски искључује (ништа не исписује) у release верзијама, јер проверава BuildConfig.DEBUG.
Forest (шума) — колекција свих посађених дрвећа. Када се позове метод Timber.d(„message”), библиотека итеративно преноси поруку свим дрвећима редом садње. Свако дрво може да филтрира поруку по нивоу, tag-у или садржају и обради је на свој начин.
Редослед садње је важан: прво посађено дрво се обрађује прво. Препоручује се садња DebugTree-ја као последњег, да би прилагођена дрвећа (нпр. Crashlytics) обрадила поруку пре него што стигне у Logcat.
Timber је безбедан за нити — сви методи су синхронизовани кроз интерни lock. Ово гарантује да се поруке из различитих нити неће измешати. Међутим, унутар прилагођеног дрвета синхронизација пада на програмера: ако дрво уписује у датотеку, потребно је користити synchronized или ReentrantLock.
// Иницијализација шуме дрвећа у 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")
}
}
Инсталација Timber-а се врши додавањем једне зависности у build.gradle. Библиотека је објављена у Maven Central под артефактом com.jakewharton.timber:timber. Актуелна верзија за 2024. годину — 5.0.1, последње стабилно ажурирање.
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
Минимално подешавање након инсталације — садња DebugTree-ја у Application.onCreate. Без овог корака, Timber ће игнорисати све лог позиве, без бацања изузетака. Ово је безбедно подразумевано понашање: ако дрво није посађено, библиотека ради у празно, са минималним overhead-ом.
Према подацима Jake Wharton, 2023, 70% проблема са Timber-ом код нових корисника везано је за заборављену или неправилну иницијализацију. Timber не генерише грешку при одсуству дрвећа — програмери очекују да ће се логови појавити у Logcat-у, али се ништа не дешава.
За тестирање Timber пружа Timber.asTree() — метод који враћа тренутно дрво или null. Ово је згодно за проверу у unit тестовима: може се заменити дрво mock-ом и проверити да ли је лог порука послата са правилним нивоом и tag-ом.
Прилагођено дрво — главни разлог за коришћење Timber-а уместо стандардног Log API-ја. Преко преклапања метода Tree-ја могуће је усмерити логове било ког нивоа у Crashlytics, систем датотека, Remote Config или сопствени сервер.
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// Само Error и WTF за 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")
}
}
}
Методи за преклапање: isLoggable(tag, priority) — филтер који одређује да ли треба обрадити поруку (основна имплементација враћа true). log(priority, tag, message, t) — главна логика обраде. prepareLog(priority, tag, throwable, message, args) — позива се пре форматирања, омогућава измену поруке пре обраде.
Важна предност прилагођених дрвећа — одсуство рефлексије. За разлику од многих logging фрејмворкова, Timber не користи Reflection API за одређивање tag-а или нивоа. Tag се израчунава кроз анализу стека позива (Throwable.stackTrace), што ради ред величине брже.
Поређење Timber-а и стандардног Log API-ја показује четири кључне разлике: аутоматски tag, подршка за форматирање стрингова са varargs, могућност вишеструких канала излаза и безбедно понашање при одсуству иницијализације.
| Параметар | android.util.Log | Timber |
|---|---|---|
| Одређивање tag-а | Ручно, стринг константа | Аутоматски, по стеку позива |
| Форматирање | Конкатенација или String.format | Уграђени varargs + чувар места %s |
| Канали излаза | Само Logcat | Дрвећа: Logcat, датотека, Crashlytics и др. |
| Понашање без иницијализације | Увек ради | Ништа не исписује |
| Перформансе | Основни ниво | Лењиво форматирање кроз isLoggable |
Главни аргумент против Timber-а — зависност од библиотеке треће стране. За једноставан пројекат са минималним логирањем, коришћење Timber-а може бити претерано. Међутим, према подацима Google Play Console, 2024, преко 60% топ-1000 апликација у Google Play-у користи Timber, што потврђује његову поузданост и ефикасност.
Перформансе Timber-а у release верзијама нису лошије од стандардног Log API-ја. При одсуству посађених дрвећа, метод Timber.d() проверава присуство дрвећа (један if) и враћа се — без форматирања стринга. Ово је брже од Log.d() са конкатенацијом, која се извршава увек.
Прво правило — увек проверавајте иницијализацију Timber-а у тестовима. Користите Timber.asTree() за верификацију да је дрво посађено. У unit тестовима садите TestTree који чува поруке у листи за assert провере.
Друго правило — немојте мешати Timber и android.util.Log у истом пројекту. Ако пројекат већ користи Timber, сви нови лог позиви треба да пролазе кроз њега. Мешање доводи до дуплирања порука и забуне при анализи.
Треће правило — садите CrashReportingTree без провере BuildConfig.DEBUG. За разлику од DebugTree-ја, crash дрво треба да ради и у debug и у release режиму — ово гарантује да грешке тестирања такође стигну у crash-reporting систем.
Четврто правило — користите уграђене нивое Timber-а: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf(). Избегавајте директан позив Timber.log() са нумеричким priority — ово смањује читљивост кода и отежава рефакторинг.
Пето правило — за библиотеке и модуле користите Timber.tag(„CustomTag“). Овај метод враћа привремено дрво са преклопљеним tag-ом, не утичући на глобалну конфигурацију. Ово омогућава логирање из библиотечког кода са прилагођеним идентификатором.
Често постављана питања
Да — Timber је безбедан за коришћење у библиотекама. Ако дрво није посађено у апликацији, позиви Timber-а не изазивају грешке. За библиотеке се препоручује коришћење Timber.tag(„LibraryTag“) за идентификацију извора логова.
Кроз стек позива (stack trace) — DebugTree се подиже 8 оквира изнад тачке позива Timber.d() и извлачи име класе. Метод Throwable.stackTrace се користи за одређивање класе позиваоца без трошкова Reflection API-ја.
Logcat — системски алат Android-а за преглед логова. Timber — библиотека за писање логова. Timber исписује поруке у Logcat кроз DebugTree, али такође може да их шаље у датотеке, Crashlytics, Sentry и друге канале кроз прилагођена дрвећа.
Не — Timber је везан за Android SDK (android.util.Log). За KMP пројекте размотрите Kermit или Napier — мултиплатформске библиотеке за логирање са сличном архитектуром дрвећа, које раде на Android, iOS, JVM и JS.
Користите Timber.uprootAll() — метод уклања сва регистрована дрвећа. Timber.uproot(tree) уклања конкретно дрво. Ово је корисно у тестовима за ресетовање стања између тест метода.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође