Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Fixes

- Change Spring Boot, Apollo, Apollo 3, JUL, Logback, Log4j2, OpenFeign, GraphQL and Kotlin coroutines core dependencies to compileOnly ([#2837](https://github.com/getsentry/sentry-java/pull/2837))
- Fix Coroutine Context Propagation using CopyableThreadContextElement ([#2838](https://github.com/getsentry/sentry-java/pull/2838))

## 6.25.1

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ object Config {
val retrofit2 = "$retrofit2Group:retrofit:$retrofit2Version"
val retrofit2Gson = "$retrofit2Group:converter-gson:$retrofit2Version"

val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"

val fragment = "androidx.fragment:fragment-ktx:1.3.5"

Expand Down
6 changes: 5 additions & 1 deletion sentry-kotlin-extensions/api/sentry-kotlin-extensions.api
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
public final class io/sentry/kotlin/SentryContext : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/ThreadContextElement {
public final class io/sentry/kotlin/SentryContext : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/CopyableThreadContextElement {
public fun <init> ()V
public fun <init> (Lio/sentry/IHub;)V
public synthetic fun <init> (Lio/sentry/IHub;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun copyForChild ()Lkotlinx/coroutines/CopyableThreadContextElement;
public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element;
public fun mergeForChild (Lkotlin/coroutines/CoroutineContext$Element;)Lkotlin/coroutines/CoroutineContext;
public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext;
public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext;
public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Lio/sentry/IHub;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@ package io.sentry.kotlin

import io.sentry.IHub
import io.sentry.Sentry
import kotlinx.coroutines.ThreadContextElement
import kotlinx.coroutines.CopyableThreadContextElement
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext

/**
* Sentry context element for [CoroutineContext].
*/
public class SentryContext : ThreadContextElement<IHub>, AbstractCoroutineContextElement(Key) {
public class SentryContext(private val hub: IHub = Sentry.getCurrentHub().clone()) :
CopyableThreadContextElement<IHub>, AbstractCoroutineContextElement(Key) {

private companion object Key : CoroutineContext.Key<SentryContext>

private val hub: IHub = Sentry.getCurrentHub().clone()
override fun copyForChild(): CopyableThreadContextElement<IHub> {
return SentryContext(hub.clone())
}

override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext {
return overwritingElement[Key] ?: SentryContext(hub.clone())
}

override fun updateThreadContext(context: CoroutineContext): IHub {
val oldState = Sentry.getCurrentHub()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package io.sentry.kotlin

import io.sentry.Sentry
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

class SentryContextTest {
Expand Down Expand Up @@ -80,6 +83,90 @@ class SentryContextTest {
}
}

@Test
fun testContextIsClonedWhenPassedToChild() = runBlocking {
Sentry.setTag("parent", "parentValue")
launch(SentryContext()) {
Sentry.setTag("c1", "c1value")
assertEquals("c1value", getTag("c1"))
assertEquals("parentValue", getTag("parent"))
assertNull(getTag("c2"))

val c2 = launch() {
Sentry.setTag("c2", "c2value")
assertEquals("c2value", getTag("c2"))
assertEquals("parentValue", getTag("parent"))
assertNotNull(getTag("c1"))
}

c2.join()

assertNotNull(getTag("c1"))
assertNull(getTag("c2"))
}
assertNull(getTag("c1"))
assertNull(getTag("c2"))
}

@Test
fun testExplicitlyPassedContextOverridesPropagatedContext() = runBlocking {
Sentry.setTag("parent", "parentValue")
launch(SentryContext()) {
Sentry.setTag("c1", "c1value")
assertEquals("c1value", getTag("c1"))
assertEquals("parentValue", getTag("parent"))
assertNull(getTag("c2"))

val c2 = launch(
SentryContext(
Sentry.getCurrentHub().clone().also {
it.setTag("cloned", "clonedValue")
}
)
) {
Sentry.setTag("c2", "c2value")
assertEquals("c2value", getTag("c2"))
assertEquals("parentValue", getTag("parent"))
assertNotNull(getTag("c1"))
assertNotNull(getTag("cloned"))
}

c2.join()

assertNotNull(getTag("c1"))
assertNull(getTag("c2"))
assertNull(getTag("cloned"))
}
assertNull(getTag("c1"))
assertNull(getTag("c2"))
assertNull(getTag("cloned"))
}

@Test
fun `mergeForChild returns copy of initial context if Key not present`() {
val initialContextElement = SentryContext(
Sentry.getCurrentHub().clone().also {
it.setTag("cloned", "clonedValue")
}
)
val mergedContextElement = initialContextElement.mergeForChild(CoroutineName("test"))

assertNotEquals(initialContextElement, mergedContextElement)
assertNotNull((mergedContextElement)[initialContextElement.key])
}

@Test
fun `mergeForChild returns passed context`() {
val initialContextElement = SentryContext(
Sentry.getCurrentHub().clone().also {
it.setTag("cloned", "clonedValue")
}
)
val mergedContextElement = SentryContext().mergeForChild(initialContextElement)

assertEquals(initialContextElement, mergedContextElement)
}

private fun getTag(tag: String): String? {
var value: String? = null
Sentry.configureScope {
Expand Down