Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
'autoService': '1.0-rc7',

// Update WrongTimberUsageDetectorTest#innerStringFormatWithStaticImport when >= 7.1.0-alpha07
'androidPlugin': '7.0.0',
'androidPlugin': '7.0.2',
'androidTools': '30.0.0',
]

Expand Down
203 changes: 175 additions & 28 deletions timber/src/main/java/timber/log/Timber.kt
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ class Timber private constructor() {
open class DebugTree : Tree() {
private val fqcnIgnore = listOf(
Timber::class.java.name,
Timber.Companion::class.java.name,
Timber.Forest::class.java.name,
Tree::class.java.name,
DebugTree::class.java.name
Expand Down Expand Up @@ -271,110 +272,109 @@ class Timber private constructor() {
}
}

companion object Forest : Tree() {
object Forest : Tree() {
/** Log a verbose message with optional format args. */
@JvmStatic override fun v(@NonNls message: String?, vararg args: Any?) {
override fun v(@NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.v(message, *args) }
}

/** Log a verbose exception and a message with optional format args. */
@JvmStatic override fun v(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
override fun v(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.v(t, message, *args) }
}

/** Log a verbose exception. */
@JvmStatic override fun v(t: Throwable?) {
override fun v(t: Throwable?) {
treeArray.forEach { it.v(t) }
}

/** Log a debug message with optional format args. */
@JvmStatic override fun d(@NonNls message: String?, vararg args: Any?) {
override fun d(@NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.d(message, *args) }
}

/** Log a debug exception and a message with optional format args. */
@JvmStatic override fun d(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
override fun d(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.d(t, message, *args) }
}

/** Log a debug exception. */
@JvmStatic override fun d(t: Throwable?) {
override fun d(t: Throwable?) {
treeArray.forEach { it.d(t) }
}

/** Log an info message with optional format args. */
@JvmStatic override fun i(@NonNls message: String?, vararg args: Any?) {
override fun i(@NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.i(message, *args) }
}

/** Log an info exception and a message with optional format args. */
@JvmStatic override fun i(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
override fun i(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.i(t, message, *args) }
}

/** Log an info exception. */
@JvmStatic override fun i(t: Throwable?) {
override fun i(t: Throwable?) {
treeArray.forEach { it.i(t) }
}

/** Log a warning message with optional format args. */
@JvmStatic override fun w(@NonNls message: String?, vararg args: Any?) {
override fun w(@NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.w(message, *args) }
}

/** Log a warning exception and a message with optional format args. */
@JvmStatic override fun w(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
override fun w(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.w(t, message, *args) }
}

/** Log a warning exception. */
@JvmStatic override fun w(t: Throwable?) {
override fun w(t: Throwable?) {
treeArray.forEach { it.w(t) }
}

/** Log an error message with optional format args. */
@JvmStatic override fun e(@NonNls message: String?, vararg args: Any?) {
override fun e(@NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.e(message, *args) }
}

/** Log an error exception and a message with optional format args. */
@JvmStatic override fun e(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
override fun e(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.e(t, message, *args) }
}

/** Log an error exception. */
@JvmStatic override fun e(t: Throwable?) {
override fun e(t: Throwable?) {
treeArray.forEach { it.e(t) }
}

/** Log an assert message with optional format args. */
@JvmStatic override fun wtf(@NonNls message: String?, vararg args: Any?) {
override fun wtf(@NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.wtf(message, *args) }
}

/** Log an assert exception and a message with optional format args. */
@JvmStatic override fun wtf(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
override fun wtf(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.wtf(t, message, *args) }
}

/** Log an assert exception. */
@JvmStatic override fun wtf(t: Throwable?) {
override fun wtf(t: Throwable?) {
treeArray.forEach { it.wtf(t) }
}

/** Log at `priority` a message with optional format args. */
@JvmStatic override fun log(priority: Int, @NonNls message: String?, vararg args: Any?) {
override fun log(priority: Int, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.log(priority, message, *args) }
}

/** Log at `priority` an exception and a message with optional format args. */
@JvmStatic
override fun log(priority: Int, t: Throwable?, @NonNls message: String?, vararg args: Any?) {
treeArray.forEach { it.log(priority, t, message, *args) }
}

/** Log at `priority` an exception. */
@JvmStatic override fun log(priority: Int, t: Throwable?) {
override fun log(priority: Int, t: Throwable?) {
treeArray.forEach { it.log(priority, t) }
}

Expand All @@ -394,15 +394,15 @@ class Timber private constructor() {
open inline fun asTree(): Tree = this

/** Set a one-time tag for use on the next logging call. */
@JvmStatic fun tag(tag: String): Tree {
fun tag(tag: String): Tree {
for (tree in treeArray) {
tree.explicitTag.set(tag)
}
return this
}

/** Add a new logging tree. */
@JvmStatic fun plant(tree: Tree) {
fun plant(tree: Tree) {
require(tree !== this) { "Cannot plant Timber into itself." }
synchronized(trees) {
trees.add(tree)
Expand All @@ -411,7 +411,7 @@ class Timber private constructor() {
}

/** Adds new logging trees. */
@JvmStatic fun plant(vararg trees: Tree) {
fun plant(vararg trees: Tree) {
for (tree in trees) {
requireNotNull(tree) { "trees contained null" }
require(tree !== this) { "Cannot plant Timber into itself." }
Expand All @@ -423,23 +423,23 @@ class Timber private constructor() {
}

/** Remove a planted tree. */
@JvmStatic fun uproot(tree: Tree) {
fun uproot(tree: Tree) {
synchronized(trees) {
require(trees.remove(tree)) { "Cannot uproot tree which is not planted: $tree" }
treeArray = trees.toTypedArray()
}
}

/** Remove all planted trees. */
@JvmStatic fun uprootAll() {
fun uprootAll() {
synchronized(trees) {
trees.clear()
treeArray = emptyArray()
}
}

/** Return a copy of all planted [trees][Tree]. */
@JvmStatic fun forest(): List<Tree> {
fun forest(): List<Tree> {
synchronized(trees) {
return unmodifiableList(trees.toList())
}
Expand All @@ -452,4 +452,151 @@ class Timber private constructor() {
private val trees = ArrayList<Tree>()
@Volatile private var treeArray = emptyArray<Tree>()
}

companion object {

/** Log a verbose message with optional format args. */
@JvmStatic fun v(@NonNls message: String?, vararg args: Any?) {
Forest.v(message, *args)
}

/** Log a verbose exception and a message with optional format args. */
@JvmStatic fun v(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.v(t,message, *args)
}

/** Log a verbose exception. */
@JvmStatic fun v(t: Throwable?) {
Forest.v(t)
}

/** Log a debug message with optional format args. */
@JvmStatic fun d(@NonNls message: String?, vararg args: Any?) {
Forest.d(message, *args)
}

/** Log a debug exception and a message with optional format args. */
@JvmStatic fun d(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.d(t,message, *args)
}

/** Log a debug exception. */
@JvmStatic fun d(t: Throwable?) {
Forest.d(t)
}

/** Log an info message with optional format args. */
@JvmStatic fun i(@NonNls message: String?, vararg args: Any?) {
Forest.i(message, *args)
}

/** Log an info exception and a message with optional format args. */
@JvmStatic fun i(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.i(t,message, *args)
}

/** Log an info exception. */
@JvmStatic fun i(t: Throwable?) {
Forest.i(t)
}

/** Log a warning message with optional format args. */
@JvmStatic fun w(@NonNls message: String?, vararg args: Any?) {
Forest.w(message, *args)
}

/** Log a warning exception and a message with optional format args. */
@JvmStatic fun w(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.w(t, message, *args)
}

/** Log a warning exception. */
@JvmStatic fun w(t: Throwable?) {
Forest.w(t)
}

/** Log an error message with optional format args. */
@JvmStatic fun e(@NonNls message: String?, vararg args: Any?) {
Forest.e(message, *args)
}

/** Log an error exception and a message with optional format args. */
@JvmStatic fun e(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.e(t, message, *args)
}

/** Log an error exception. */
@JvmStatic fun e(t: Throwable?) {
Forest.e(t)
}

/** Log an assert message with optional format args. */
@JvmStatic fun wtf(@NonNls message: String?, vararg args: Any?) {
Forest.wtf(message, *args)
}

/** Log an assert exception and a message with optional format args. */
@JvmStatic fun wtf(t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.wtf(t, message, *args)
}

/** Log an assert exception. */
@JvmStatic fun wtf(t: Throwable?) {
Forest.wtf(t)
}

/** Log at `priority` a message with optional format args. */
@JvmStatic fun log(priority: Int, @NonNls message: String?, vararg args: Any?) {
Forest.log(priority, message, *args)
}

/** Log at `priority` an exception and a message with optional format args. */
@JvmStatic fun log(priority: Int, t: Throwable?, @NonNls message: String?, vararg args: Any?) {
Forest.log(priority, t, message, *args)
}

/** Log at `priority` an exception. */
@JvmStatic fun log(priority: Int, t: Throwable?) {
Forest.log(priority, t)
}

/**
* A view into Timber's planted trees as a tree itself. This can be used for injecting a logger
* instance rather than using static methods or to facilitate testing.
*/
@JvmStatic
fun asTree(): Tree = Forest

/** Set a one-time tag for use on the next logging call. */
@JvmStatic fun tag(tag: String): Tree = Forest.tag(tag)

/** Add a new logging tree. */
@JvmStatic fun plant(tree: Tree) {
Forest.plant(tree)
}

/** Adds new logging trees. */
@JvmStatic fun plant(vararg trees: Tree) {
Forest.plant(*trees)
}

/** Remove a planted tree. */
@JvmStatic fun uproot(tree: Tree) {
Forest.uproot(tree)
}

/** Remove all planted trees. */
@JvmStatic fun uprootAll() {
Forest.uprootAll()
}

/** Return a copy of all planted [trees][Tree]. */
@JvmStatic fun forest(): List<Tree> {
return Forest.forest()
}

/** Return the number of planted trees */
@get:[JvmStatic JvmName("treeCount")]
val treeCount get() = Forest.treeCount
}
}
25 changes: 25 additions & 0 deletions timber/src/test/java/timber/log/TimberTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.robolectric.annotation.Config
import org.robolectric.shadows.ShadowLog

import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.robolectric.shadows.ShadowLog.LogItem
Expand Down Expand Up @@ -545,6 +546,30 @@ class TimberTest {
.hasDebugMessage("TimberTest", "Test formatting: Test message logged. 100")
}

@Test fun staticRefUsage() {
fun invokeStaticRefWithException(funcRef: (Throwable?) -> Unit) {
funcRef(truncatedThrowable(Exception::class.java))
}

fun invokeStaticRefWithTree(funcRef: (Timber.Tree) -> Unit) {
funcRef(object : Timber.DebugTree() {
override fun formatMessage(message: String, vararg args: Any?): String {
return ""
}
})
}

invokeStaticRefWithTree(Timber::plant)

invokeStaticRefWithException(Timber::v)
invokeStaticRefWithException(Timber::d)
invokeStaticRefWithException(Timber::w)
invokeStaticRefWithException(Timber::e)
invokeStaticRefWithException(Timber::wtf)

assertEquals(5, getLogs().size)
}

private fun <T : Throwable> truncatedThrowable(throwableClass: Class<T>): T {
val throwable = throwableClass.newInstance()
val stackTrace = throwable.stackTrace
Expand Down