A logger with a small, extensible API which provides utility on top of Android's normal Log class
npx @tessl/cli install tessl/maven-com-jakewharton-timber--timber@5.0.00
# Timber
1
2
Timber is a logger with a small, extensible API which provides utility on top of Android's normal Log class. It uses a Tree-based architecture where logging behavior is added through Tree instances that can be planted into the Timber system. The library automatically determines the calling class for log tags and supports string formatting with proper exception handling.
3
4
## Package Information
5
6
- **Package Name**: com.jakewharton.timber:timber
7
- **Package Type**: Android Library (Gradle/Maven)
8
- **Language**: Kotlin
9
- **Installation**: Add `implementation 'com.jakewharton.timber:timber:5.0.1'` to your build.gradle
10
11
## Core Imports
12
13
```kotlin
14
import timber.log.Timber
15
```
16
17
## Basic Usage
18
19
```kotlin
20
import timber.log.Timber
21
22
// In your Application class onCreate()
23
if (BuildConfig.DEBUG) {
24
Timber.plant(Timber.DebugTree())
25
}
26
27
// Throughout your app
28
Timber.d("Debug message")
29
Timber.i("Info message with %s", "formatting")
30
Timber.w(exception, "Warning with exception")
31
Timber.e(exception)
32
```
33
34
## Architecture
35
36
Timber is built around several key components:
37
38
- **Timber Class**: Main static API (Forest companion object) that delegates to planted Tree instances
39
- **Tree Abstract Class**: Base class for logging implementations with all logging methods
40
- **DebugTree**: Concrete implementation that automatically infers tags from calling class
41
- **Tree Management**: Plant/uproot system for adding and removing logging behavior
42
- **Thread-Local Tagging**: Support for one-time custom tags per logging call
43
44
## Capabilities
45
46
### Static Logging Methods
47
48
All logging methods are available as static calls that delegate to all planted Tree instances.
49
50
```kotlin { .api }
51
// Verbose logging
52
fun v(message: String?, vararg args: Any?)
53
fun v(t: Throwable?, message: String?, vararg args: Any?)
54
fun v(t: Throwable?)
55
56
// Debug logging
57
fun d(message: String?, vararg args: Any?)
58
fun d(t: Throwable?, message: String?, vararg args: Any?)
59
fun d(t: Throwable?)
60
61
// Info logging
62
fun i(message: String?, vararg args: Any?)
63
fun i(t: Throwable?, message: String?, vararg args: Any?)
64
fun i(t: Throwable?)
65
66
// Warning logging
67
fun w(message: String?, vararg args: Any?)
68
fun w(t: Throwable?, message: String?, vararg args: Any?)
69
fun w(t: Throwable?)
70
71
// Error logging
72
fun e(message: String?, vararg args: Any?)
73
fun e(t: Throwable?, message: String?, vararg args: Any?)
74
fun e(t: Throwable?)
75
76
// Assert logging
77
fun wtf(message: String?, vararg args: Any?)
78
fun wtf(t: Throwable?, message: String?, vararg args: Any?)
79
fun wtf(t: Throwable?)
80
81
// Generic priority logging
82
fun log(priority: Int, message: String?, vararg args: Any?)
83
fun log(priority: Int, t: Throwable?, message: String?, vararg args: Any?)
84
fun log(priority: Int, t: Throwable?)
85
```
86
87
**Usage Examples:**
88
89
```kotlin
90
import timber.log.Timber
91
92
// Basic logging with string formatting
93
Timber.d("User %s logged in with ID %d", username, userId)
94
95
// Exception logging
96
try {
97
riskyOperation()
98
} catch (e: Exception) {
99
Timber.e(e, "Failed to perform risky operation")
100
// Or just log the exception
101
Timber.e(e)
102
}
103
104
// Different log levels
105
Timber.v("Verbose details")
106
Timber.i("Information message")
107
Timber.w("Warning message")
108
Timber.wtf("What a Terrible Failure!")
109
110
// Custom priority levels
111
Timber.log(Log.DEBUG, "Custom priority debug message")
112
```
113
114
### Tree Management
115
116
Methods for adding and removing logging implementations.
117
118
```kotlin { .api }
119
/**
120
* Add a new logging tree
121
* @param tree Tree instance to add to the forest
122
*/
123
fun plant(tree: Tree)
124
125
/**
126
* Add multiple logging trees
127
* @param trees Vararg array of Tree instances to add
128
*/
129
fun plant(vararg trees: Tree)
130
131
/**
132
* Remove a planted tree
133
* @param tree Tree instance to remove from the forest
134
*/
135
fun uproot(tree: Tree)
136
137
/**
138
* Remove all planted trees
139
*/
140
fun uprootAll()
141
142
/**
143
* Return a copy of all planted trees
144
* @return Unmodifiable list of currently planted trees
145
*/
146
fun forest(): List<Tree>
147
148
/**
149
* Get the number of currently planted trees
150
*/
151
val treeCount: Int
152
```
153
154
**Usage Examples:**
155
156
```kotlin
157
import timber.log.Timber
158
159
// Plant trees for different environments
160
if (BuildConfig.DEBUG) {
161
Timber.plant(Timber.DebugTree())
162
} else {
163
// Plant custom production trees
164
Timber.plant(CrashlyticsTree(), FileLoggingTree())
165
}
166
167
// Remove specific tree
168
val debugTree = Timber.DebugTree()
169
Timber.plant(debugTree)
170
// Later...
171
Timber.uproot(debugTree)
172
173
// Clear all trees
174
Timber.uprootAll()
175
176
// Check planted trees
177
val currentTrees = Timber.forest()
178
val treeCount = Timber.treeCount
179
```
180
181
### Tag Management
182
183
Support for one-time custom tags for specific logging calls.
184
185
```kotlin { .api }
186
/**
187
* Set a one-time tag for use on the next logging call
188
* @param tag Custom tag to use for the next log message
189
* @return Tree instance for method chaining
190
*/
191
fun tag(tag: String): Tree
192
```
193
194
**Usage Examples:**
195
196
```kotlin
197
import timber.log.Timber
198
199
// Use custom tag for specific log message
200
Timber.tag("CUSTOM_TAG").d("Debug message with custom tag")
201
202
// Chain with logging call
203
Timber.tag("NETWORK").i("API request completed")
204
```
205
206
### Utility Methods
207
208
Additional utility methods for accessing Timber as a Tree instance.
209
210
```kotlin { .api }
211
/**
212
* Get Timber's Forest as a Tree instance for dependency injection
213
* @return Tree instance representing all planted trees
214
*/
215
fun asTree(): Tree
216
```
217
218
**Usage Examples:**
219
220
```kotlin
221
import timber.log.Timber
222
223
// Inject Timber as a Tree dependency
224
class ApiClient(private val logger: Tree = Timber.asTree()) {
225
fun makeRequest() {
226
logger.d("Making API request")
227
}
228
}
229
```
230
231
## Tree Abstract Class
232
233
Base class for implementing custom logging behavior.
234
235
```kotlin { .api }
236
abstract class Tree {
237
// All logging methods (same signatures as static methods)
238
open fun v(message: String?, vararg args: Any?)
239
open fun v(t: Throwable?, message: String?, vararg args: Any?)
240
open fun v(t: Throwable?)
241
242
open fun d(message: String?, vararg args: Any?)
243
open fun d(t: Throwable?, message: String?, vararg args: Any?)
244
open fun d(t: Throwable?)
245
246
open fun i(message: String?, vararg args: Any?)
247
open fun i(t: Throwable?, message: String?, vararg args: Any?)
248
open fun i(t: Throwable?)
249
250
open fun w(message: String?, vararg args: Any?)
251
open fun w(t: Throwable?, message: String?, vararg args: Any?)
252
open fun w(t: Throwable?)
253
254
open fun e(message: String?, vararg args: Any?)
255
open fun e(t: Throwable?, message: String?, vararg args: Any?)
256
open fun e(t: Throwable?)
257
258
open fun wtf(message: String?, vararg args: Any?)
259
open fun wtf(t: Throwable?, message: String?, vararg args: Any?)
260
open fun wtf(t: Throwable?)
261
262
open fun log(priority: Int, message: String?, vararg args: Any?)
263
open fun log(priority: Int, t: Throwable?, message: String?, vararg args: Any?)
264
open fun log(priority: Int, t: Throwable?)
265
266
// Protected methods for customization
267
@Deprecated("Use isLoggable(String, int)", ReplaceWith("this.isLoggable(null, priority)"))
268
protected open fun isLoggable(priority: Int): Boolean
269
protected open fun isLoggable(tag: String?, priority: Int): Boolean
270
protected open fun formatMessage(message: String, args: Array<out Any?>): String
271
272
// Abstract method that must be implemented
273
protected abstract fun log(priority: Int, tag: String?, message: String, t: Throwable?)
274
275
// Internal properties
276
internal val explicitTag: ThreadLocal<String>
277
internal open val tag: String?
278
}
279
```
280
281
**Custom Tree Implementation Example:**
282
283
```kotlin
284
import timber.log.Timber
285
import android.util.Log
286
287
class CustomTree : Timber.Tree() {
288
override fun isLoggable(tag: String?, priority: Int): Boolean {
289
// Only log warnings and errors in production
290
return priority >= Log.WARN
291
}
292
293
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
294
// Send to custom logging service
295
MyLoggingService.log(priority, tag, message, t)
296
}
297
}
298
299
// Usage
300
Timber.plant(CustomTree())
301
```
302
303
## DebugTree Implementation
304
305
Concrete Tree implementation for debug builds that automatically infers tags.
306
307
```kotlin { .api }
308
open class DebugTree : Tree() {
309
/**
310
* Extract the tag from a stack trace element
311
* @param element Stack trace element to extract tag from
312
* @return Extracted tag name, may be null
313
*/
314
protected open fun createStackElementTag(element: StackTraceElement): String?
315
316
/**
317
* Log implementation that handles long messages and uses Android Log
318
* @param priority Log priority level
319
* @param tag Log tag (may be null)
320
* @param message Formatted log message
321
* @param t Optional throwable
322
*/
323
override fun log(priority: Int, tag: String?, message: String, t: Throwable?)
324
325
companion object {
326
private const val MAX_LOG_LENGTH = 4000
327
private const val MAX_TAG_LENGTH = 23
328
}
329
}
330
```
331
332
**DebugTree Customization Example:**
333
334
```kotlin
335
import timber.log.Timber
336
337
class CustomDebugTree : Timber.DebugTree() {
338
override fun createStackElementTag(element: StackTraceElement): String? {
339
// Include line number in tag
340
return "${super.createStackElementTag(element)}:${element.lineNumber}"
341
}
342
}
343
344
// Usage
345
Timber.plant(CustomDebugTree())
346
```
347
348
## Constants and Priority Levels
349
350
Timber uses Android Log priority constants:
351
352
```kotlin { .api }
353
// Android Log priority constants (from android.util.Log)
354
const val VERBOSE = 2
355
const val DEBUG = 3
356
const val INFO = 4
357
const val WARN = 5
358
const val ERROR = 6
359
const val ASSERT = 7
360
```
361
362
## Error Handling
363
364
Timber handles various error conditions gracefully:
365
366
- **Null messages**: If message is null and no throwable, the log call is swallowed
367
- **Empty messages**: If message is empty, throwable stack trace is used as message
368
- **Unplanted trees**: Calling `uproot()` on a non-planted tree throws `IllegalArgumentException`
369
- **Self-planting**: Attempting to plant Timber into itself throws `IllegalArgumentException`
370
- **Long messages**: DebugTree automatically chunks messages longer than 4000 characters
371
- **Thread safety**: Tree management operations are synchronized for thread safety
372
373
## Common Patterns
374
375
### Application Setup
376
377
```kotlin
378
import timber.log.Timber
379
380
class MyApplication : Application() {
381
override fun onCreate() {
382
super.onCreate()
383
384
if (BuildConfig.DEBUG) {
385
Timber.plant(Timber.DebugTree())
386
} else {
387
// Plant production trees
388
Timber.plant(CrashlyticsTree(), FileLoggingTree())
389
}
390
}
391
}
392
```
393
394
### Activity Logging
395
396
```kotlin
397
import timber.log.Timber
398
399
class MainActivity : AppCompatActivity() {
400
override fun onCreate(savedInstanceState: Bundle?) {
401
super.onCreate(savedInstanceState)
402
Timber.d("MainActivity created")
403
404
try {
405
setupViews()
406
} catch (e: Exception) {
407
Timber.e(e, "Failed to setup views")
408
}
409
}
410
411
private fun setupViews() {
412
Timber.v("Setting up views")
413
// Implementation
414
}
415
}
416
```
417
418
### Network Logging
419
420
```kotlin
421
import timber.log.Timber
422
423
class ApiClient {
424
fun makeRequest(url: String) {
425
Timber.tag("NETWORK").d("Making request to %s", url)
426
427
try {
428
val response = httpClient.get(url)
429
Timber.tag("NETWORK").i("Request successful: %d", response.code)
430
} catch (e: IOException) {
431
Timber.tag("NETWORK").e(e, "Request failed for %s", url)
432
}
433
}
434
}
435
```