Skip to content

Commit bc43364

Browse files
abendtAlphonse Bendt
authored and
Alphonse Bendt
committed
Kotlin#119: implementation of a SLF4J MDC Context
1 parent 5f2e94b commit bc43364

File tree

7 files changed

+199
-0
lines changed

7 files changed

+199
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Module kotlinx-coroutines-slf4j
2+
3+
Integration with SLF4J [MDC](https://logback.qos.ch/manual/mdc.html).
4+
5+
## Example
6+
7+
Extends a given Coroutine context so that the SLF4J MDC context is passed into the coroutine.
8+
9+
```kotlin
10+
MDC.put("kotlin", "rocks") // put a value into the MDC context
11+
12+
launch(MDCContext(CommonPool)) {
13+
logger.info { "..." } // the MDC context will contain the mapping here
14+
}
15+
```
16+
17+
# Package kotlinx.coroutines.experimental.slf4j
18+
19+
Integration with SLF4J [MDC](https://logback.qos.ch/manual/mdc.html).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
dependencies {
2+
compile 'org.slf4j:slf4j-api:1.7.25'
3+
testCompile 'io.github.microutils:kotlin-logging:1.5.4'
4+
testRuntime 'ch.qos.logback:logback-classic:1.2.3'
5+
testRuntime 'ch.qos.logback:logback-core:1.2.3'
6+
}
7+
8+
tasks.withType(dokka.getClass()) {
9+
externalDocumentationLink {
10+
url = new URL("https://www.slf4j.org/apidocs/")
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
contextMap?.let {
29+
MDC.setContextMap(contextMap)
30+
}
31+
32+
block()
33+
} finally {
34+
MDC.clear()
35+
}
36+
}
37+
38+
override val context: CoroutineContext get() = continuation.context
39+
override fun resume(value: T) = wrap { continuation.resume(value) }
40+
override fun resumeWithException(exception: Throwable) = wrap { continuation.resumeWithException(exception) }
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package kotlinx.coroutines.experimental.slf4j
2+
3+
import kotlinx.coroutines.experimental.*
4+
import org.junit.After
5+
import org.junit.Before
6+
import org.junit.Test
7+
import org.slf4j.MDC
8+
import kotlin.test.assertEquals
9+
import kotlin.test.assertNull
10+
11+
class MDCContextTest : TestBase() {
12+
13+
@Before
14+
fun setUp() {
15+
MDC.clear()
16+
}
17+
18+
@After
19+
fun tearDown() {
20+
MDC.clear()
21+
}
22+
23+
@Test
24+
fun mdcContextIsNotPassedByDefaultBetweenCoRoutines() = runTest {
25+
expect(1)
26+
MDC.put("myKey", "myValue")
27+
28+
launch {
29+
val mdcValue = MDC.get("myKey")
30+
check(mdcValue == null)
31+
expect(2)
32+
}.join()
33+
34+
expect(3)
35+
36+
finish(4)
37+
}
38+
39+
@Test
40+
fun mdcContextCanBePassedBetweenCoRoutines() = runTest {
41+
expect(1)
42+
MDC.put("myKey", "myValue")
43+
44+
launch(MDCContext(DefaultDispatcher)) {
45+
val mdcValue = MDC.get("myKey")
46+
check(mdcValue == "myValue")
47+
expect(2)
48+
}.join()
49+
50+
expect(3)
51+
52+
finish(4)
53+
}
54+
55+
@Test
56+
fun mdcContextNotNeededWhileOnMainThread() {
57+
MDC.put("myKey", "myValue")
58+
59+
runBlocking {
60+
val mdcValue = MDC.get("myKey")
61+
62+
assertEquals(mdcValue, "myValue")
63+
}
64+
}
65+
66+
@Test
67+
fun mdcContextNeededWithOtherContext() {
68+
MDC.put("myKey", "myValue")
69+
70+
runBlocking(MDCContext(DefaultDispatcher)) {
71+
val mdcValue = MDC.get("myKey")
72+
73+
assertEquals(mdcValue, "myValue")
74+
}
75+
}
76+
77+
@Test
78+
fun mdcContextMayBeEmpty() {
79+
runBlocking(MDCContext(DefaultDispatcher)) {
80+
val mdcValue = MDC.get("myKey")
81+
82+
assertNull(mdcValue)
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package kotlinx.coroutines.experimental.slf4j
2+
3+
import mu.KotlinLogging
4+
import org.slf4j.MDC
5+
6+
fun main(args: Array<String>) {
7+
val logger = KotlinLogging.logger {}
8+
9+
// You can put values in the MDC at any time. Before anything else
10+
// we put the first name
11+
MDC.put("first", "Dorothy")
12+
13+
// We now put the last name
14+
MDC.put("last", "Parker")
15+
16+
// The most beautiful two words in the English language according
17+
// to Dorothy Parker:
18+
logger.info("Check enclosed.")
19+
logger.debug("The most beautiful two words in English.")
20+
21+
MDC.put("first", "Richard")
22+
MDC.put("last", "Nixon")
23+
logger.info("I am not a crook.")
24+
logger.info("Attributed to the former US president. 17 Nov 1973.")
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration debug="false">
3+
4+
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
5+
<layout>
6+
<Pattern>%X{first} %X{last} - %m%n</Pattern>
7+
</layout>
8+
</appender>
9+
10+
<root level="DEBUG">
11+
<appender-ref ref="CONSOLE" />
12+
</root>
13+
</configuration>
14+
15+

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)