Timber — چیست، API کتابخانه و نمونه‌های استفاده

نویسنده: IT Sectr منتشر شده: 2026-05-28 زمان مطالعه: 8 دقیقه

Timber — کتابخانه سبک لاگ‌گیری برای Android با معماری قابل گسترش مبتنی بر درختان (Tree) است که android.util.Log استاندارد را در هزاران پروژه جایگزین کرده است. طبق داده‌های GitHub, 2024، این کتابخانه بیش از 10٬000 ستاره جمع‌آوری کرده و در برنامه‌هایی با مخاطب بیش از 1 میلیارد نصب استفاده می‌شود. Timber سه مشکل اصلی Log API را حل می‌کند: عدم وجود tag خودکار، بررسی اجباری isLoggable و ماهیت ایستای فراخوانی‌ها.

نکات اصلی

  • Timber — wrapper بر روی android.util.Log با تشخیص خودکار tag بر اساس نام کلاس و پشته فراخوانی
  • Tree — عنصر پایه معماری Timber، هر نمونه نحوه پردازش پیام لاگ را مشخص می‌کند
  • کاشت درختان (Planting) — فرآیند ثبت Tree در Timber، معمولاً یک بار در Application.onCreate انجام می‌شود
  • DebugTree — پیاده‌سازی داخلی برای نسخه‌های Debug، لاگ‌ها را با tag از نام کلاس به Logcat خروجی می‌دهد
  • Custom Tree — امکان ایجاد پیاده‌سازی سفارشی برای ارسال لاگ‌ها به Crashlytics، فایل یا سرور

Timber چیست

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 کیلوبایت در APK اشغال می‌کند و وابستگی خارجی ندارد که آن را به گزینه‌ای ایده‌آل برای پروژه‌های هر مقیاسی تبدیل می‌کند.

Timber مشکل tagهای ناسازگار در تیم‌های بزرگ را حل می‌کند. وقتی هر توسعه‌دهنده tag را دستی می‌نویسد، اشتباهات تایپی و تفاوت‌ها اجتناب‌ناپذیر است — یک کلاس به عنوان «MainActivity» لاگ می‌شود، دیگری به عنوان «MAIN_ACTIVITY». Timber به طور خودکار tag را از نام کلاس استخراج می‌کند:MainActivity.kt → tag MainActivity.

معماری Timber: درختان و جنگل

معماری Timber از دو مؤلفه تشکیل شده است: کلاس ایستای مرکزی Timber و کلاس انتزاعی Timber.Tree. Timber به عنوان facade عمل می‌کند که هر فراخوانی لاگ را به همه درختان کاشته شده (planted) واگذار می‌کند. هر درخت تصمیم می‌گیرد که آیا باید پیام را پردازش کند و اگر بله — به کجا هدایت کند.

DebugTree — پیاده‌سازی داخلی برای توسعه

DebugTree — پیاده‌سازی استاندارد Tree که با کتابخانه ارائه می‌شود. این پیاده‌سازی tag را از طریق تحلیل پشته فراخوانی تعیین می‌کند: 8 فریم از نقطه فراخوانی Timber.d() بالا رفته و نام کلاسی که متد لاگ را فراخوانی کرده پیدا می‌کند. DebugTree به طور خودکار در نسخه‌های release غیرفعال می‌شود (چیزی خروجی نمی‌دهد) زیرا BuildConfig.DEBUG را بررسی می‌کند.

اصول کار «جنگل»

Forest (جنگل) — مجموعه تمام درختان کاشته شده. وقتی متد Timber.d(«message») فراخوانی می‌شود، کتابخانه به صورت تکراری پیام را به همه درختان به ترتیب کاشت منتقل می‌کند. هر درخت می‌تواند پیام را بر اساس سطح، tag یا محتوا فیلتر کرده و به روش خود پردازش کند.

ترتیب کاشت مهم است: اولین درخت کاشته شده اول پردازش می‌شود. توصیه می‌شود DebugTree را آخرین بار بکارید تا درختان سفارشی (مثلاً Crashlytics) پیام را قبل از رسیدن به Logcat پردازش کنند.

ایمنی نخ (Thread safety)

Timber ایمن از نظر نخ است — همه متدها از طریق قفل داخلی همگام‌سازی شده‌اند. این تضمین می‌کند که پیام‌های نخ‌های مختلف با هم مخلوط نشوند. با این حال، در داخل درخت سفارشی همگام‌سازی بر عهده توسعه‌دهنده است: اگر درخت در فایل می‌نویسد، باید از synchronized یا ReentrantLock استفاده کرد.

kotlin
// راه‌اندازی جنگل درختان در 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 در پروژه Android

نصب Timber با افزودن یک وابستگی به build.gradle انجام می‌شود. کتابخانه در Maven Central با artifact com.jakewharton.timber:timber منتشر شده است. نسخه فعلی در سال 2024 — 5.0.1، آخرین به‌روزرسانی پایدار.

groovy
// 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 را برمی‌گرداند. این برای بررسی در تست‌های واحد مفید است: می‌توان درخت را با mock جایگزین کرد و بررسی کرد که پیام لاگ با سطح و tag صحیح ارسال شده است.

ایجاد Tree سفارشی برای پردازش لاگ‌های دلخواه

درخت سفارشی — دلیل اصلی استفاده از Timber به جای Log API استاندارد. با override کردن متدهای Tree می‌توان لاگ‌های هر سطحی را به Crashlytics، سیستم فایل، Remote Config یا سرور خود هدایت کرد.

kotlin
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")
        }
    }
}

متدهای قابل override: isLoggable(tag, priority) — فیلتری که تعیین می‌کند آیا باید پیام پردازش شود (پیاده‌سازی پایه true برمی‌گرداند). log(priority, tag, message, t) — منطق اصلی پردازش. prepareLog(priority, tag, throwable, message, args) — قبل از قالب‌بندی فراخوانی می‌شود، امکان تغییر پیام قبل از پردازش را می‌دهد.

مزیت مهم درختان سفارشی — عدم استفاده از reflection. بر خلاف بسیاری از فریم‌ورک‌های لاگ‌گیری، Timber از Reflection API برای تعیین tag یا سطح استفاده نمی‌کند. Tag از طریق تحلیل پشته فراخوانی (Throwable.stackTrace) محاسبه می‌شود که مرتبه‌ای سریع‌تر کار می‌کند.

Timber در مقابل android.util.Log استاندارد

مقایسه Timber و Log API استاندارد چهار تفاوت کلیدی را نشان می‌دهد: tag خودکار، پشتیبانی از قالب‌بندی رشته با varargs، امکان کانال‌های خروجی چندگانه و رفتار ایمن در صورت عدم راه‌اندازی.

پارامترandroid.util.LogTimber
تعیین tagدستی، ثابت رشته‌ایخودکار، بر اساس پشته فراخوانی
قالب‌بندیالحاق یا String.formatvarargs داخلی + جایگیر %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() با الحاق است که همیشه انجام می‌شود.

Best practices در استفاده از Timber

قانون اول — همیشه راه‌اندازی Timber را در تست‌ها بررسی کنید. از Timber.asTree() برای تأیید کاشت درخت استفاده کنید. در تست‌های واحد، 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 عددی خودداری کنید — این کار خوانایی کد را کاهش می‌دهد و refactoring را پیچیده می‌کند.

قانون پنجم — برای کتابخانه‌ها و ماژول‌ها از Timber.tag(«CustomTag») استفاده کنید. این متد یک درخت موقت با tag بازنویسی شده برمی‌گرداند بدون تأثیر بر پیکربندی سراسری. این امکان لاگ‌گیری از کد کتابخانه با شناسه سفارشی را فراهم می‌کند.

سؤالات متداول

آیا می‌توان از Timber در ماژول کتابخانه Android استفاده کرد؟

بله — Timber برای استفاده در کتابخانه‌ها ایمن است. اگر درختی در برنامه کاشته نشده باشد، فراخوانی‌های Timber خطا ایجاد نمی‌کنند. برای کتابخانه‌ها توصیه می‌شود از Timber.tag(«LibraryTag») برای شناسایی منبع لاگ‌ها استفاده شود.

Timber چگونه tag را بدون تعیین دستی تشخیص می‌دهد؟

از طریق پشته فراخوانی (stack trace) — DebugTree 8 فریم از نقطه فراخوانی Timber.d() بالا رفته و نام کلاس را استخراج می‌کند. متد Throwable.stackTrace برای تعیین کلاس فراخوان بدون هزینه Reflection API استفاده می‌شود.

Timber چه تفاوتی با Logcat دارد؟

Logcat — ابزار سیستمی Android برای مشاهده لاگ‌ها. Timber — کتابخانه برای نوشتن لاگ‌ها. Timber پیام‌ها را از طریق DebugTree به Logcat خروجی می‌دهد، اما همچنین می‌تواند آنها را از طریق درختان سفارشی به فایل‌ها، Crashlytics، Sentry و سایر کانال‌ها ارسال کند.

آیا Timber از Kotlin Multiplatform پشتیبانی می‌کند؟

خیر — Timber به Android SDK (android.util.Log) وابسته است. برای پروژه‌های KMP، Kermit یا Napier را در نظر بگیرید — کتابخانه‌های لاگ‌گیری چندسکویی با معماری درختی مشابه که روی Android، iOS، JVM و JS کار می‌کنند.

چگونه همه درختان کاشته شده در Timber را حذف کنیم؟

از Timber.uprootAll() استفاده کنید — این متد تمام درختان ثبت‌شده را حذف می‌کند. Timber.uproot(tree) یک درخت خاص را حذف می‌کند. این در تست‌ها برای بازنشانی وضعیت بین متدهای تست مفید است.

نتیجه‌گیری

  • Timber — wrapper سبک بر روی android.util.Log با tag خودکار و معماری درختی
  • Tree — عنصر پایه، هر درخت کانال خروجی لاگ خود را تعیین می‌کند
  • DebugTree — پیاده‌سازی داخلی برای Logcat، به طور خودکار در release غیرفعال می‌شود
  • Custom Tree — لاگ‌ها را به Crashlytics، فایل‌ها، سرور یا هر کانال دیگری ارسال می‌کند
  • Timber.tag() — تغییر موقت tag برای کد کتابخانه بدون پیکربندی سراسری
  • ایمنی نخ — همه متدهای Timber همگام‌سازی شده‌اند، درختان سفارشی نیاز به همگام‌سازی خود دارند
  • سکوت ایمن — در صورت نبود درختان، Timber استثنا پرتاب نمی‌کند و منابع مصرف نمی‌کند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید