Timber — co to jest, API biblioteki i przykłady użycia

Autor: IT Sectr Opublikowano: 2026-05-28 Czas czytania: 8 min

Timber — lekka biblioteka logowania dla Android z rozszerzalną architekturą opartą na drzewach (Tree), która zastąpiła standardowy android.util.Log w tysiącach projektów. Według danych GitHub, 2024, biblioteka zdobyła ponad 10 000 gwiazdek i jest używana w aplikacjach z publicznością ponad 1 miliarda instalacji. Timber rozwiązuje trzy główne problemy Log API: brak automatycznego tagu, obowiązkowe sprawdzanie isLoggable i statyczny charakter wywołań.

Najważniejsze

  • Timber — nakładka na android.util.Log z automatycznym określaniem tagu na podstawie nazwy klasy i stosu wywołań
  • Tree — podstawowy element architektury Timber, każda instancja określa, jak przetwarzać komunikat logu
  • Planting drzew — proces rejestracji Tree w Timber, zwykle wykonywany raz w Application.onCreate
  • DebugTree — wbudowana implementacja dla wersji debug, wypisuje logi do Logcat z tagiem z nazwy klasy
  • Custom Tree — możliwość stworzenia własnej implementacji do wysyłania logów do Crashlytics, pliku lub na serwer

Czym jest Timber

Timber — to biblioteka open-source dla Android, stworzona przez Jake'a Whartona w 2013 roku jako alternatywa dla standardowego android.util.Log. Kluczową ideą Timber jest zastąpienie statycznego Log API z obowiązkowym ręcznym tagiem automatycznym mechanizmem, który określa źródło wywołania na podstawie stosu.

Biblioteka jest zbudowana na wzorcu architektonicznym Composite z drzewami (Tree). Zamiast pojedynczej klasy Log ze stałym zachowaniem, Timber zarządza „lasem” z drzew — każde drzewo odpowiada za swój kanał wyjścia: konsolę, plik, Crashlytics, zdalny serwer. Deweloper może dodać dowolną liczbę drzew i je łączyć.

Według danych Google I/O 2019, Timber jest rekomendowany przez Google jako najlepsza praktyka logowania w aplikacjach Android. Biblioteka zajmuje mniej niż 10 KB w APK i nie ma zewnętrznych zależności, co czyni ją idealnym wyborem dla projektów każdej skali.

Timber rozwiązuje problem niespójnych tagów w dużych zespołach. Gdy każdy deweloper pisze tag ręcznie, nieuniknione są literówki i rozbieżności — jedna klasa loguje się jako „MainActivity”, inna jako „MAIN_ACTIVITY”. Timber automatycznie wyprowadza tag z nazwy klasy:MainActivity.kt → tag MainActivity.

Architektura Timber: drzewa i las

Architektura Timber składa się z dwóch komponentów: centralnej statycznej klasy Timber i abstrakcyjnej klasy Timber.Tree. Timber pełni rolę fasady, która deleguje każde wywołanie logu do wszystkich posadzonych (planted) drzew. Każde drzewo decyduje, czy przetworzyć komunikat, a jeśli tak — dokąd go skierować.

DebugTree — wbudowana implementacja do rozwoju

DebugTree — standardowa implementacja Tree dostarczana z biblioteką. Określa tag poprzez analizę stosu wywołań: podnosi się o 8 ramek w górę od punktu wywołania Timber.d() i znajduje nazwę klasy, która wywołała metodę logowania. DebugTree automatycznie wyłącza się (nic nie wypisuje) w wersjach release, ponieważ sprawdza BuildConfig.DEBUG.

Zasada działania „lasu”

Forest (las) — kolekcja wszystkich posadzonych drzew. Gdy wywoływana jest metoda Timber.d(„message”), biblioteka iteracyjnie przekazuje komunikat do wszystkich drzew w kolejności ich sadzenia. Każde drzewo może odfiltrować komunikat według poziomu, tagu lub treści i przetworzyć go na swój sposób.

Kolejność sadzenia jest ważna: pierwsze posadzone drzewo przetwarzane jest jako pierwsze. Zaleca się sadzenie DebugTree jako ostatniego, aby niestandardowe drzewa (np. Crashlytics) przetworzyły komunikat zanim trafi on do Logcat.

Bezpieczeństwo wątkowe

Timber jest bezpieczny wątkowo — wszystkie metody są synchronizowane przez wewnętrzną blokadę. Gwarantuje to, że komunikaty z różnych wątków nie pomieszają się. Jednak wewnątrz niestandardowego drzewa synchronizacja spoczywa na deweloperze: jeśli drzewo pisze do pliku, należy użyć synchronized lub ReentrantLock.

kotlin
// Inicjalizacja lasu drzew w 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")
    }
}

Instalacja i konfiguracja Timber w projekcie Android

Instalacja Timber polega na dodaniu jednej zależności w build.gradle. Biblioteka jest opublikowana w Maven Central pod artefaktem com.jakewharton.timber:timber. Aktualna wersja na 2024 rok — 5.0.1, ostatnia stabilna aktualizacja.

groovy
// build.gradle (Module: app)
dependencies {
    implementation 'com.jakewharton.timber:timber:5.0.1'
}

Minimalna konfiguracja po instalacji — posadzenie DebugTree w Application.onCreate. Bez tego kroku Timber będzie ignorować wszystkie wywołania logowania, nie rzucając wyjątków. Jest to bezpieczne domyślne zachowanie: jeśli drzewo nie jest posadzone, biblioteka pracuje na pusto, z minimalnym narzutem.

Według danych Jake Wharton, 2023, 70% problemów z Timber u nowych użytkowników jest związanych z zapomnianą lub nieprawidłową inicjalizacją. Timber nie generuje błędu przy braku drzew — deweloperzy oczekują, że logi pojawią się w Logcat, ale nic się nie dzieje.

Do testowania Timber udostępnia Timber.asTree() — metodę zwracającą bieżące drzewo lub null. Jest to wygodne do sprawdzania w testach jednostkowych: można podmienić drzewo na mock i sprawdzić, czy komunikat logu został wysłany z prawidłowym poziomem i tagiem.

Tworzenie własnego Tree do niestandardowego przetwarzania logów

Niestandardowe drzewo — główny powód używania Timber zamiast standardowego Log API. Poprzez nadpisanie metod Tree można skierować logi dowolnego poziomu do Crashlytics, systemu plików, Remote Config lub własnego serwera.

kotlin
class CrashReportingTree : Timber.Tree() {

    override fun isLoggable(tag: String?, priority: Int): Boolean {
        // Tylko Error i WTF do 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")
        }
    }
}

Metody do nadpisania: isLoggable(tag, priority) — filtr określający, czy przetworzyć komunikat (podstawowa implementacja zwraca true). log(priority, tag, message, t) — główna logika przetwarzania. prepareLog(priority, tag, throwable, message, args) — wywoływana przed formatowaniem, pozwala zmienić komunikat przed jego przetworzeniem.

Ważną zaletą niestandardowych drzew jest brak refleksji. W przeciwieństwie do wielu frameworków logowania, Timber nie używa Reflection API do określania tagu ani poziomu. Tag jest obliczany poprzez analizę stosu wywołań (Throwable.stackTrace), co działa o rząd wielkości szybciej.

Timber vs standardowy android.util.Log

Porównanie Timber i standardowego Log API pokazuje cztery kluczowe różnice: automatyczny tag, wsparcie formatowania ciągów z varargs, możliwość wielu kanałów wyjścia i bezpieczne zachowanie przy braku inicjalizacji.

Parametrandroid.util.LogTimber
Określenie taguRęczne, stała ciągAutomatyczne, na podstawie stosu wywołań
FormatowanieKonkatenacja lub String.formatWbudowany varargs + placeholder %s
Kanały wyjściaTylko LogcatDrzewa: Logcat, plik, Crashlytics i inne
Zachowanie bez inicjalizacjiDziała zawszeNic nie wypisuje
WydajnośćPodstawowy poziomLeniwę formatowanie przez isLoggable

Główny argument przeciwko Timber — zależność od zewnętrznej biblioteki. Dla prostego projektu z minimalnym logowaniem używanie Timber może być przesadne. Jednak według danych Google Play Console, 2024, ponad 60% top-1000 aplikacji w Google Play używa Timber, co potwierdza jego niezawodność i skuteczność.

Wydajność Timber w wersjach release nie ustępuje standardowemu Log API. Przy braku posadzonych drzew metoda Timber.d() sprawdza obecność drzew (jeden if) i zwraca się — bez formatowania ciągu. Jest to szybsze niż Log.d() z konkatenacją, która wykonuje się zawsze.

Best practices przy używaniu Timber

Pierwsza zasada — zawsze sprawdzaj inicjalizację Timber w testach. Używaj Timber.asTree() do weryfikacji, że drzewo jest posadzone. W testach jednostkowych sadź TestTree, który zapisuje komunikaty na liście do asercji.

Druga zasada — nie mieszaj Timber i android.util.Log w jednym projekcie. Jeśli projekt już używa Timber, wszystkie nowe wywołania logowania powinny przechodzić przez niego. Mieszanie prowadzi do duplikowania komunikatów i zamieszania podczas analizy.

Trzecia zasada — sadź CrashReportingTree bez sprawdzania BuildConfig.DEBUG. W przeciwieństwie do DebugTree, drzewo crash powinno działać zarówno w debug, jak i w release — gwarantuje to, że błędy testowania również trafią do systemu crash-reporting.

Czwarta zasada — używaj wbudowanych poziomów Timber: Timber.v(), Timber.d(), Timber.i(), Timber.w(), Timber.e(), Timber.wtf(). Unikaj bezpośredniego wywoływania Timber.log() z numerycznym priority — zmniejsza to czytelność kodu i utrudnia refaktoryzację.

Piąta zasada — dla bibliotek i modułów używaj Timber.tag(„CustomTag”). Ta metoda zwraca tymczasowe drzewo z nadpisanym tagiem, nie wpływając na globalną konfigurację. Pozwala to logować z kodu bibliotecznego z niestandardowym identyfikatorem.

Często zadawane pytania

Czy można używać Timber w module bibliotecznym Android?

Tak — Timber jest bezpieczny do używania w bibliotekach. Jeśli drzewo nie jest posadzone w aplikacji, wywołania Timber nie powodują błędów. Dla bibliotek zaleca się używanie Timber.tag(„LibraryTag”) do identyfikacji źródła logów.

Jak Timber określa tag bez ręcznego wskazania?

Przez stos wywołań (stack trace) — DebugTree podnosi się o 8 ramek w górę od punktu wywołania Timber.d() i wyodrębnia nazwę klasy. Metoda Throwable.stackTrace jest używana do określenia klasy wywołującej bez kosztów Reflection API.

Czym Timber różni się od Logcat?

Logcat — systemowe narzędzie Android do przeglądania logów. Timber — biblioteka do pisania logów. Timber wypisuje komunikaty do Logcat przez DebugTree, ale może również wysyłać je do plików, Crashlytics, Sentry i innych kanałów przez niestandardowe drzewa.

Czy Timber obsługuje Kotlin Multiplatform?

Nie — Timber jest związany z Android SDK (android.util.Log). Dla projektów KMP rozważ Kermit lub Napier — wieloplatformowe biblioteki logowania z podobną architekturą drzew, działające na Android, iOS, JVM i JS.

Jak usunąć wszystkie posadzone drzewa w Timber?

Użyj Timber.uprootAll() — metoda usuwa wszystkie zarejestrowane drzewa. Timber.uproot(tree) usuwa konkretne drzewo. Jest to przydatne w testach do resetowania stanu między metodami testowymi.

Podsumowanie

  • Timber — lekka nakładka na android.util.Log z automatycznym tagiem i architekturą drzew
  • Tree — podstawowy element, każde drzewo określa swój kanał wyjścia logów
  • DebugTree — wbudowana implementacja dla Logcat, automatycznie wyłączana w release
  • Custom Tree — wysyła logi do Crashlytics, plików, serwera lub dowolnego innego kanału
  • Timber.tag() — tymczasowa zmiana tagu dla kodu bibliotecznego bez globalnej konfiguracji
  • Bezpieczeństwo wątkowe — wszystkie metody Timber są synchronizowane, niestandardowe drzewa wymagają własnej synchronizacji
  • Bezpieczna cisza — przy braku drzew Timber nie rzuca wyjątków i nie zużywa zasobów

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również