Skip to content

Commit e0feeb9

Browse files
committed
Kotlin#119: first implementation of a SLF4J MDC Context
1 parent 5f2e94b commit e0feeb9

File tree

4 files changed

+114
-0
lines changed

4 files changed

+114
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
dependencies {
2+
compile 'org.slf4j:slf4j-api:1.7.25'
3+
testRuntime 'ch.qos.logback:logback-classic:1.2.3'
4+
testRuntime 'ch.qos.logback:logback-core:1.2.3'
5+
}
6+
7+
tasks.withType(dokka.getClass()) {
8+
externalDocumentationLink {
9+
url = new URL("https://www.slf4j.org/apidocs/")
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package kotlinx.coroutines.experimental.slf4j
2+
3+
import org.slf4j.MDC
4+
import kotlin.coroutines.experimental.AbstractCoroutineContextElement
5+
import kotlin.coroutines.experimental.Continuation
6+
import kotlin.coroutines.experimental.ContinuationInterceptor
7+
import kotlin.coroutines.experimental.CoroutineContext
8+
9+
class MDCContext(
10+
private val context: CoroutineContext,
11+
private val contextMap: Map<String, String> = MDC.getCopyOfContextMap()
12+
) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
13+
14+
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
15+
val next = context[ContinuationInterceptor]
16+
17+
val wrappedContinuation = Wrapper(continuation)
18+
19+
return when (next) {
20+
null -> wrappedContinuation
21+
else -> next.interceptContinuation(wrappedContinuation)
22+
}
23+
}
24+
25+
private inner class Wrapper<T>(private val continuation: Continuation<T>) : Continuation<T> {
26+
private inline fun wrap(block: () -> Unit) {
27+
try {
28+
MDC.setContextMap(contextMap)
29+
block()
30+
} finally {
31+
MDC.clear()
32+
}
33+
}
34+
35+
override val context: CoroutineContext get() = continuation.context
36+
override fun resume(value: T) = wrap { continuation.resume(value) }
37+
override fun resumeWithException(exception: Throwable) = wrap { continuation.resumeWithException(exception) }
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package kotlinx.coroutines.experimental.slf4j
2+
3+
import kotlinx.coroutines.experimental.*
4+
import org.junit.Test
5+
import org.slf4j.MDC
6+
import kotlin.test.assertEquals
7+
8+
class MDCContextTest : TestBase() {
9+
10+
@Test
11+
fun mdcContextIsNotPassedByDefaultBetweenCoRoutines() = runTest {
12+
expect(1)
13+
MDC.put("myKey", "myValue")
14+
15+
launch {
16+
val mdcValue = MDC.get("myKey")
17+
check(mdcValue == null)
18+
expect(2)
19+
}.join()
20+
21+
expect(3)
22+
23+
finish(4)
24+
}
25+
26+
@Test
27+
fun mdcContextCanBePassedBetweenCoRoutines() = runTest {
28+
expect(1)
29+
MDC.put("myKey", "myValue")
30+
31+
launch(MDCContext(DefaultDispatcher)) {
32+
val mdcValue = MDC.get("myKey")
33+
check(mdcValue == "myValue")
34+
expect(2)
35+
}.join()
36+
37+
expect(3)
38+
39+
finish(4)
40+
}
41+
42+
@Test
43+
fun mdcContextNotNeededWhileOnMainThread() {
44+
MDC.put("myKey", "myValue")
45+
46+
runBlocking {
47+
val mdcValue = MDC.get("myKey")
48+
49+
assertEquals(mdcValue, "myValue")
50+
}
51+
}
52+
53+
@Test
54+
fun mdcContextNeededWithOtherContext() {
55+
MDC.put("myKey", "myValue")
56+
57+
runBlocking(MDCContext(DefaultDispatcher)) {
58+
val mdcValue = MDC.get("myKey")
59+
60+
assertEquals(mdcValue, "myValue")
61+
}
62+
}
63+
}

settings.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module('integration/kotlinx-coroutines-guava')
2424
module('integration/kotlinx-coroutines-jdk8')
2525
module('integration/kotlinx-coroutines-nio')
2626
module('integration/kotlinx-coroutines-quasar')
27+
module('integration/kotlinx-coroutines-slf4j')
2728

2829
module('reactive/kotlinx-coroutines-reactive')
2930
module('reactive/kotlinx-coroutines-reactor')

0 commit comments

Comments
 (0)