Timberは、拡張可能なツリー(Tree)ベースのアーキテクチャを備えたAndroid向けの軽量ロギングライブラリで、何千ものプロジェクトで標準のandroid.util.Logを置き換えています。GitHub、2024によると、このライブラリは10,000以上のスターを獲得し、10億以上のインストール数を持つアプリケーションで使用されています。TimberはLog APIの3つの主要な問題(自動タグの欠如、必須のisLoggableチェック、呼び出しの静的性質)を解決します。
重要ポイント
Timberは、Jake Whartonが2013年に標準のandroid.util.Logの代替として作成したAndroid向けのオープンソースライブラリです。Timberの核となるアイデアは、必須の手動タグを持つ静的Log APIを、スタックを介して呼び出し元を自動的に特定するメカニズムに置き換えることです。
このライブラリはComposite with Treesアーキテクチャパターンに基づいて構築されています。固定された動作を持つ単一のLogクラスの代わりに、Timberはツリーの「森(フォレスト)」を管理します。各ツリーは、コンソール、ファイル、Crashlytics、リモートサーバーといった独自の出力チャネルを担当します。開発者は任意の数のツリーを追加して組み合わせることができます。
Google I/O 2019によると、TimberはGoogleによってAndroidアプリケーションのロギングのベストプラクティスとして推奨されています。このライブラリはAPKで10 KB未満を占め、外部依存関係がないため、あらゆる規模のプロジェクトに最適な選択肢です。
Timberは大規模チームにおける一貫性のないタグの問題を解決します。各開発者が手動でタグを書くと、タイプミスや不一致が避けられません。あるクラスは「MainActivity」として記録され、別のクラスは「MAIN_ACTIVITY」として記録されます。Timberはクラス名から自動的にタグを導出します:MainActivity.kt → タグ MainActivity。
アーキテクチャは、中央の静的クラスTimberと抽象クラスTimber.Treeの2つのコンポーネントで構成されています。Timberはファサードとして機能し、各ログ呼び出しを植え付けられたすべてのツリーに委任します。各ツリーはメッセージを処理するかどうか、また処理する場合はどこに送信するかを決定します。
DebugTreeはライブラリに付属する標準のTree実装です。呼び出しスタックを分析してタグを特定します。Timber.d()の呼び出しポイントから8フレーム上に移動し、ログメソッドを呼び出したクラス名を見つけます。DebugTreeはBuildConfig.DEBUGをチェックするため、リリースビルドでは自動的に無効になります(何も出力しません)。
Forest(フォレスト) — 植え付けられたすべてのツリーのコレクション。Timber.d("message")メソッドが呼び出されると、ライブラリはメッセージを植え付けられた順にすべてのツリーに反復的に渡します。各ツリーはレベル、タグ、または内容に基づいてメッセージをフィルタリングし、独自の方法で処理できます。
植え付けの順序は重要です。最初に植え付けられたツリーが最初に処理されます。カスタムツリー(例:Crashlytics)がLogcatに到達する前にメッセージを処理できるように、DebugTreeは最後に植え付けることをお勧めします。
Timberはスレッドセーフです。すべてのメソッドは内部ロックを介して同期されます。これにより、異なるスレッドからのメッセージが混ざらないことが保証されます。ただし、カスタムツリー内では、同期は開発者の責任です。ツリーがファイルに書き込む場合は、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")
}
}
インストールは、build.gradleに1つの依存関係を追加するだけです。ライブラリはMaven Centralにcom.jakewharton.timber:timberというアーティファクトで公開されています。2024年現在のバージョンは5.0.1で、最新の安定版アップデートです。
// build.gradle (Module: app)
dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
}
インストール後の最小限の設定 — Application.onCreateでDebugTreeを植え付けます。この手順がないと、Timberは例外をスローせずにすべてのログ呼び出しを無視します。これは安全なデフォルト動作です。ツリーが植え付けられていない場合、ライブラリは最小限のオーバーヘッドでアイドル状態で動作します。
Jake Wharton、2023によると、新しいユーザーのTimberの問題の70%は、初期化の忘れや誤った初期化に関連しています。ツリーがない場合、Timberはエラーを生成しません。開発者はLogcatにログが表示されることを期待しますが、何も起こりません。
テストのために、TimberはTimber.asTree()を提供しています。これは現在のツリーまたはnullを返すメソッドです。これは単体テストに便利です。ツリーをモックに置き換えて、ログメッセージが正しいレベルとタグで送信されたことを確認できます。
カスタムツリー — 標準のLog APIの代わりにTimberを使用する主な理由です。Treeメソッドをオーバーライドすることで、あらゆるレベルのログをCrashlytics、ファイルシステム、Remote Config、または独自のサーバーにルーティングできます。
class CrashReportingTree : Timber.Tree() {
override fun isLoggable(tag: String?, priority: Int): Boolean {
// クラッシュレポート用のErrorとWTFのみ
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) — フォーマットの前に呼び出され、処理前にメッセージを変更できます。
カスタムツリーの重要な利点はリフレクションがないことです。多くのロギングフレームワークとは異なり、Timberはタグやレベルを特定するためにReflection APIを使用しません。タグは呼び出しスタック(Throwable.stackTrace)の分析を通じて計算され、桁違いに高速に動作します。
比較 Timberと標準のLog APIの比較では、4つの主な違いが示されています:自動タグ、varargsによる文字列フォーマットのサポート、複数の出力チャネル、初期化なしの安全な動作。
| パラメータ | android.util.Log | Timber |
|---|---|---|
| タグの特定 | 手動、文字列定数 | 自動、呼び出しスタック経由 |
| フォーマット | 連結またはString.format | 組み込みvarargs + %sプレースホルダー |
| 出力チャネル | Logcatのみ | ツリー:Logcat、ファイル、Crashlyticsなど |
| 初期化なしの動作 | 常に動作 | 何も出力しない |
| パフォーマンス | ベースラインレベル | isLoggableによる遅延フォーマット |
Timberに対する主な反論 — サードパーティライブラリへの依存。最小限のロギングしか必要としないシンプルなプロジェクトでは、Timberの使用は過剰かもしれません。しかし、Google Play Console、2024によると、Google Playのトップ1000アプリの60%以上がTimberを使用しており、その信頼性と効率性が確認されています。
リリースビルドでのTimberのパフォーマンスは標準のLog APIと同等です。ツリーが植え付けられていない場合、Timber.d()メソッドはツリーの存在を確認し(1つのif)、文字列フォーマットを行わずに戻ります。これは常に実行される連結を使用するLog.d()よりも高速です。
第一のルール — テストで常にTimberの初期化を確認してください。Timber.asTree()を使用してツリーが植え付けられていることを確認します。単体テストでは、アサートチェック用にメッセージをリストに保存するTestTreeを植え付けます。
第二のルール — 同じプロジェクトでTimberとandroid.util.Logを混在させないでください。プロジェクトがすでにTimberを使用している場合、すべての新しいログ呼び出しはそれを通す必要があります。混在させると、メッセージが重複し、分析時に混乱を招きます。
第三のルール — BuildConfig.DEBUGをチェックせずにCrashReportingTreeを植え付けます。DebugTreeとは異なり、クラッシュツリーはデバッグとリリースの両方で動作する必要があります。これにより、テストエラーもクラッシュレポートシステムで確実にキャプチャされます。
第四のルール — 組み込みのTimberレベル(Timber.v()、Timber.d()、Timber.i()、Timber.w()、Timber.e()、Timber.wtf())を使用します。数値の優先度を指定してTimber.log()を直接呼び出すことは避けてください。コードの可読性が低下し、リファクタリングが複雑になります。
第五のルール — ライブラリやモジュールには、Timber.tag("CustomTag")を使用します。このメソッドは、グローバル設定に影響を与えずにオーバーライドされたタグを持つ一時的なツリーを返します。これにより、カスタム識別子を使用してライブラリコードからロギングできます。
よくある質問
はい — Timberはライブラリで安全に使用できます。アプリケーションにツリーが植え付けられていない場合、Timberの呼び出しはエラーを発生させません。ライブラリの場合は、ログソースを特定するためにTimber.tag("LibraryTag")を使用することをお勧めします。
呼び出しスタック(stack trace)を介して — DebugTreeはTimber.d()の呼び出しポイントから8フレーム上に移動し、クラス名を抽出します。Throwable.stackTraceメソッドを使用して、Reflection APIのオーバーヘッドなしで呼び出し元のクラスを特定します。
Logcatはログを表示するためのAndroidシステムユーティリティです。Timberはログを書くためのライブラリです。TimberはDebugTreeを介してLogcatにメッセージを出力しますが、カスタムツリーを介してファイル、Crashlytics、Sentry、その他のチャネルにも送信できます。
いいえ — TimberはAndroid SDK(android.util.Log)に依存しています。KMPプロジェクトの場合は、KermitまたはNapierを検討してください。これらは同様のツリーアーキテクチャを持つマルチプラットフォームロギングライブラリで、Android、iOS、JVM、JSで動作します。
Timber.uprootAll()を使用します。このメソッドは登録されたすべてのツリーを削除します。Timber.uproot(tree)は特定のツリーを削除します。これはテストメソッド間で状態をリセットするためにテストで便利です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。