From 960a3d5e7c64b18d6ddf014375c7d99d868f6b33 Mon Sep 17 00:00:00 2001 From: Timothy Nibeaudeau Date: Wed, 29 Sep 2021 16:45:45 +0200 Subject: [PATCH 1/3] Bump AGP to 7.0.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e78acb6f8..9ab9c778d 100644 --- a/build.gradle +++ b/build.gradle @@ -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', ] From 8f83198c0591331137a34e6861ecf68a125329bb Mon Sep 17 00:00:00 2001 From: Timothy Nibeaudeau Date: Wed, 29 Sep 2021 16:46:36 +0200 Subject: [PATCH 2/3] Make Timber compatible with function reference in Kotlin --- timber/src/main/java/timber/log/Timber.kt | 202 +++++++++++++++++++--- 1 file changed, 174 insertions(+), 28 deletions(-) diff --git a/timber/src/main/java/timber/log/Timber.kt b/timber/src/main/java/timber/log/Timber.kt index 85a551b26..21710990f 100644 --- a/timber/src/main/java/timber/log/Timber.kt +++ b/timber/src/main/java/timber/log/Timber.kt @@ -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 @@ -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) } } @@ -394,7 +394,7 @@ 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) } @@ -402,7 +402,7 @@ class Timber private constructor() { } /** 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) @@ -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." } @@ -423,7 +423,7 @@ 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() @@ -431,7 +431,7 @@ class Timber private constructor() { } /** Remove all planted trees. */ - @JvmStatic fun uprootAll() { + fun uprootAll() { synchronized(trees) { trees.clear() treeArray = emptyArray() @@ -439,7 +439,7 @@ class Timber private constructor() { } /** Return a copy of all planted [trees][Tree]. */ - @JvmStatic fun forest(): List { + fun forest(): List { synchronized(trees) { return unmodifiableList(trees.toList()) } @@ -452,4 +452,150 @@ class Timber private constructor() { private val trees = ArrayList() @Volatile private var treeArray = emptyArray() } + + 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 { + return Forest.forest() + } + + @get:[JvmStatic JvmName("treeCount")] + val treeCount get() = Forest.treeCount + } } From be2a7cb1ca5b3bae9715aacae719feb09e614abe Mon Sep 17 00:00:00 2001 From: Timothy Nibeaudeau Date: Wed, 29 Sep 2021 17:19:54 +0200 Subject: [PATCH 3/3] Add test for usage of static ref --- timber/src/main/java/timber/log/Timber.kt | 1 + timber/src/test/java/timber/log/TimberTest.kt | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/timber/src/main/java/timber/log/Timber.kt b/timber/src/main/java/timber/log/Timber.kt index 21710990f..f1dd03599 100644 --- a/timber/src/main/java/timber/log/Timber.kt +++ b/timber/src/main/java/timber/log/Timber.kt @@ -595,6 +595,7 @@ class Timber private constructor() { return Forest.forest() } + /** Return the number of planted trees */ @get:[JvmStatic JvmName("treeCount")] val treeCount get() = Forest.treeCount } diff --git a/timber/src/test/java/timber/log/TimberTest.kt b/timber/src/test/java/timber/log/TimberTest.kt index cbfecf236..4ad737f4e 100644 --- a/timber/src/test/java/timber/log/TimberTest.kt +++ b/timber/src/test/java/timber/log/TimberTest.kt @@ -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 @@ -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 truncatedThrowable(throwableClass: Class): T { val throwable = throwableClass.newInstance() val stackTrace = throwable.stackTrace