diff --git a/CHANGELOG.md b/CHANGELOG.md index fe4c599b6f..47043d9106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Fix tag extraction logic for Timber ([#4489](https://github.com/getsentry/sentry-java/pull/4489)) + ## 8.14.0 ### Fixes diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 236044c981..0824487b27 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -144,7 +144,7 @@ springboot3-starter-aop = { module = "org.springframework.boot:spring-boot-start springboot3-starter-security = { module = "org.springframework.boot:spring-boot-starter-security", version.ref = "springboot3" } springboot3-starter-jdbc = { module = "org.springframework.boot:spring-boot-starter-jdbc", version.ref = "springboot3" } springboot3-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "springboot3" } -timber = { module = "com.jakewharton.timber:timber", version = "4.7.1" } +timber = { module = "com.jakewharton.timber:timber", version = "5.0.1" } # test libraries androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4", version = "1.6.8" } diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt index dda29c61d8..084524778f 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt @@ -1,3 +1,5 @@ +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + package io.sentry.android.timber import android.util.Log @@ -17,23 +19,15 @@ public class SentryTimberTree( private val minEventLevel: SentryLevel, private val minBreadcrumbLevel: SentryLevel ) : Timber.Tree() { - private val pendingTag = ThreadLocal() - - private fun retrieveTag(): String? { - val tag = pendingTag.get() - if (tag != null) { - this.pendingTag.remove() - } - return tag - } /** Log a verbose message with optional format args. */ override fun v( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.v(message, *args) - logWithSentry(Log.VERBOSE, null, message, *args) + logWithSentry(Log.VERBOSE, null, message, tag, *args) } /** Log a verbose exception and a message with optional format args. */ @@ -42,14 +36,16 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.v(t, message, *args) - logWithSentry(Log.VERBOSE, t, message, *args) + logWithSentry(Log.VERBOSE, t, message, tag, *args) } /** Log a verbose exception. */ override fun v(t: Throwable?) { + val tag = explicitTag.get() super.v(t) - logWithSentry(Log.VERBOSE, t, null) + logWithSentry(Log.VERBOSE, t, null, tag, null) } /** Log a debug message with optional format args. */ @@ -57,8 +53,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.d(message, *args) - logWithSentry(Log.DEBUG, null, message, *args) + logWithSentry(Log.DEBUG, null, message, tag, *args) } /** Log a debug exception and a message with optional format args. */ @@ -67,14 +64,16 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.d(t, message, *args) - logWithSentry(Log.DEBUG, t, message, *args) + logWithSentry(Log.DEBUG, t, message, tag, *args) } /** Log a debug exception. */ override fun d(t: Throwable?) { + val tag = explicitTag.get() super.d(t) - logWithSentry(Log.DEBUG, t, null) + logWithSentry(Log.DEBUG, t, null, tag, null) } /** Log an info message with optional format args. */ @@ -82,8 +81,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.d(message, *args) - logWithSentry(Log.INFO, null, message, *args) + logWithSentry(Log.INFO, null, message, tag, *args) } /** Log an info exception and a message with optional format args. */ @@ -92,14 +92,16 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.i(t, message, *args) - logWithSentry(Log.INFO, t, message, *args) + logWithSentry(Log.INFO, t, message, tag, *args) } /** Log an info exception. */ override fun i(t: Throwable?) { + val tag = explicitTag.get() super.i(t) - logWithSentry(Log.INFO, t, null) + logWithSentry(Log.INFO, t, null, tag, null) } /** Log a warning message with optional format args. */ @@ -107,8 +109,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.w(message, *args) - logWithSentry(Log.WARN, null, message, *args) + logWithSentry(Log.WARN, null, message, tag, *args) } /** Log a warning exception and a message with optional format args. */ @@ -117,14 +120,16 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.w(t, message, *args) - logWithSentry(Log.WARN, t, message, *args) + logWithSentry(Log.WARN, t, message, tag, *args) } /** Log a warning exception. */ override fun w(t: Throwable?) { + val tag = explicitTag.get() super.w(t) - logWithSentry(Log.WARN, t, null) + logWithSentry(Log.WARN, t, null, tag, null) } /** Log an error message with optional format args. */ @@ -132,8 +137,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.e(message, *args) - logWithSentry(Log.ERROR, null, message, *args) + logWithSentry(Log.ERROR, null, message, tag, *args) } /** Log an error exception and a message with optional format args. */ @@ -142,14 +148,16 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.e(t, message, *args) - logWithSentry(Log.ERROR, t, message, *args) + logWithSentry(Log.ERROR, t, message, tag, *args) } /** Log an error exception. */ override fun e(t: Throwable?) { + val tag = explicitTag.get() super.e(t) - logWithSentry(Log.ERROR, t, null) + logWithSentry(Log.ERROR, t, null, tag, null) } /** Log an assert message with optional format args. */ @@ -157,8 +165,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.wtf(message, *args) - logWithSentry(Log.ASSERT, null, message, *args) + logWithSentry(Log.ASSERT, null, message, tag, *args) } /** Log an assert exception and a message with optional format args. */ @@ -167,14 +176,16 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.wtf(t, message, *args) - logWithSentry(Log.ASSERT, t, message, *args) + logWithSentry(Log.ASSERT, t, message, tag, *args) } /** Log an assert exception. */ override fun wtf(t: Throwable?) { + val tag = explicitTag.get() super.wtf(t) - logWithSentry(Log.ASSERT, t, null) + logWithSentry(Log.ASSERT, t, null, tag, null) } /** Log at `priority` a message with optional format args. */ @@ -183,8 +194,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.log(priority, message, *args) - logWithSentry(priority, null, message, *args) + logWithSentry(priority, null, message, tag, *args) } /** Log at `priority` an exception and a message with optional format args. */ @@ -194,8 +206,9 @@ public class SentryTimberTree( message: String?, vararg args: Any? ) { + val tag = explicitTag.get() super.log(priority, t, message, *args) - logWithSentry(priority, t, message, *args) + logWithSentry(priority, t, message, tag, *args) } /** Log at `priority` an exception. */ @@ -204,26 +217,16 @@ public class SentryTimberTree( t: Throwable? ) { super.log(priority, t) - logWithSentry(priority, t, null) - } - - override fun log( - priority: Int, - tag: String?, - message: String, - t: Throwable? - ) { - pendingTag.set(tag) + logWithSentry(priority, t, null, tag, null) } private fun logWithSentry( priority: Int, throwable: Throwable?, message: String?, + tag: String?, vararg args: Any? ) { - val tag = retrieveTag() - if (message.isNullOrEmpty() && throwable == null) { return // Swallow message if it's null and there's no throwable } @@ -238,7 +241,7 @@ public class SentryTimberTree( } captureEvent(level, tag, sentryMessage, throwable) - addBreadcrumb(level, sentryMessage, throwable) + addBreadcrumb(level, sentryMessage, tag, throwable) } /** @@ -279,12 +282,13 @@ public class SentryTimberTree( private fun addBreadcrumb( sentryLevel: SentryLevel, msg: Message, + tag: String?, throwable: Throwable? ) { // checks the breadcrumb level if (isLoggable(sentryLevel, minBreadcrumbLevel)) { val throwableMsg = throwable?.message - val breadCrumb = when { + val breadcrumb = when { msg.message != null -> Breadcrumb().apply { level = sentryLevel category = "Timber" @@ -295,11 +299,24 @@ public class SentryTimberTree( } else -> null } - - breadCrumb?.let { scopes.addBreadcrumb(it) } + if (breadcrumb != null) { + if (tag != null) { + breadcrumb.setData("tag", tag) + } + scopes.addBreadcrumb(breadcrumb) + } } } + override fun log( + priority: Int, + tag: String?, + message: String, + t: Throwable? + ) { + // no-op + } + /** * Converts from Timber priority to SentryLevel. * Fallback to SentryLevel.DEBUG. diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt index 8bb85aa085..91c6a37fc3 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt @@ -43,7 +43,7 @@ class SentryTimberIntegrationTest { val sut = fixture.getSut() sut.register(fixture.scopes, fixture.options) - assertEquals(1, Timber.treeCount()) + assertEquals(1, Timber.treeCount) val trees = Timber.forest() val first = trees.first() @@ -64,10 +64,10 @@ class SentryTimberIntegrationTest { val sut = fixture.getSut() sut.register(fixture.scopes, fixture.options) - assertEquals(1, Timber.treeCount()) + assertEquals(1, Timber.treeCount) sut.close() - assertEquals(0, Timber.treeCount()) + assertEquals(0, Timber.treeCount) } @Test @@ -75,7 +75,7 @@ class SentryTimberIntegrationTest { val sut = fixture.getSut() sut.close() - assertEquals(0, Timber.treeCount()) + assertEquals(0, Timber.treeCount) } @Test diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt index 30afeb928d..b6f38c4839 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt @@ -269,6 +269,45 @@ class SentryTimberTreeTest { ) } + @Test + fun `Tree adds tag to breadcrumb data`() { + val sut = fixture.getSut() + Timber.plant(sut) + + // when a tag is set via tag API + Timber.tag("errorTag") + sut.e("error") + + // it should be added to the breadcrumb + verify(fixture.scopes).addBreadcrumb( + check { + assertEquals("error", it.message) + assertEquals("errorTag", it.data["tag"]) + } + ) + + // when a tag is set via method chaining + Timber.tag("warnTag").w("warn") + + // it should be added to the breadcrumb + verify(fixture.scopes).addBreadcrumb( + check { + assertEquals("warn", it.message) + assertEquals("warnTag", it.data["tag"]) + } + ) + + // when no tag is set + Timber.w("warn without tag") + // it should not have a tag in the breadcrumb + verify(fixture.scopes).addBreadcrumb( + check { + assertEquals("warn without tag", it.message) + assertNull(it.data["tag"]) + } + ) + } + @Test fun `Tree does not add a breadcrumb, if no message provided`() { val sut = fixture.getSut()