sentry-java icon indicating copy to clipboard operation
sentry-java copied to clipboard

Sentry <> Timber integration: Missing `TimberTag` tag

Open gent-ahmeti opened this issue 8 months ago • 5 comments

Integration

sentry-android

Build System

Gradle

AGP Version

8.6.0

Proguard

Enabled

Sentry Version

7.4.0

Timber Version

5.0.1

Steps to Reproduce

  1. Report issue like so Timber.tag("MyTag").w(throwable, "Something went wrong") (It's possible that it might be working for Timber.e())
  2. Open Sentry dashboard and locate the aforementioned issue

Expected Result

In the list of tags we should see TimberTag tag with the valueMyTag

As mentioned by the changelog, TimberTag is supported from version 5.7.2 onwards

Support for Timber.tag has been brought back in version 5.7.2.

Actual Result

When checking tags you can notice that TimberTag tag is missing completely. See attached screenshot with what I mean by tag Image

gent-ahmeti avatar Jun 11 '25 12:06 gent-ahmeti

@gent-ahmeti thanks for opening this up! We'll have a look into this. Could also share the version of Timber you're using?

markushi avatar Jun 11 '25 12:06 markushi

Hi @markushi , we are using Timber version 5.0.1. I'll update the description to have this version too

gent-ahmeti avatar Jun 11 '25 12:06 gent-ahmeti

@gent-ahmeti thank you! Could you also share some more details on how you adapted it for the usage with Coroutines? Timber itself utilizes ThreadLocal for tags which could cause troubles with Coroutines:

https://github.com/JakeWharton/timber/blob/beb8051248164a74b264d30427f633aaf4bda841/timber/src/main/java/timber/log/Timber.kt#L19-L23

markushi avatar Jun 11 '25 13:06 markushi

Looks like we are catching the "tag" before we launch the coroutine. See abbreviated code:

private val pendingTag = ThreadLocal<String?>()

private fun retrieveTag(): String? {
    val tag = pendingTag.get()
    if (tag != null) {
        this.pendingTag.remove()
    }
    return tag
}

private fun logWithSentry(
        priority: Int,
        throwable: Throwable?,
        message: String?,
        vararg args: Any?
    ) {
        val tag = retrieveTag()
        // ...
        captureEvent(level, tag, sentryMessage, throwable)
        // ...
    }
// ...
    private fun captureEvent(
        sentryLevel: SentryLevel,
        tag: String?,
        msg: Message,
        throwable: Throwable?
    ) {
        if (isLoggable(sentryLevel, minEventLevel)) {
            val sentryEvent = SentryEvent().apply {
                // ...
                tag?.let {
                    setTag("TimberTag", it)
                }
                // ...
            }
           // ...
            coroutineScope.launch(AppDispatchers.computation) {
                Sentry.captureEvent(sentryEvent)
            }
        }
    }

gent-ahmeti avatar Jun 11 '25 14:06 gent-ahmeti

@gent-ahmeti awesome, thanks for sharing! This should give us enough context to reproduce the issue. I'll keep you posted about any updates!

markushi avatar Jun 11 '25 19:06 markushi

Long overdue update: We're currently blocked by the internal ThreadLocal usage within Timber itself, as it's not exposing any APIs to provide ways to make the tag value coroutine-aware. Blocked by: https://github.com/JakeWharton/timber/issues/565

markushi avatar Aug 04 '25 11:08 markushi

@gent-ahmeti Could you share a few code snippets on how you're using Timber.tag() within your codebase? As outlined here Timber.tag() should be immediately followed by a .log() call, otherwise you could run into coroutine related issues.

E.g. the following wouldn't work:

Timber.tag("example-tag")
try {
  // ... suspend fun(), e.g. io bound 
} catch (e: Exception) {
  Timber.error(...)
}

markushi avatar Aug 05 '25 10:08 markushi

Hi @markushi thanks for the updates. We always use it in a chain like so:

Timber.tag(TAG).e(exception)
// or
Timber.tag(TAG).w(exception)

gent-ahmeti avatar Aug 07 '25 08:08 gent-ahmeti