Skip to content

Commit 83fa0b4

Browse files
committed
Supply MDC context propagation with examples.
Fixes #3280
1 parent 1d04452 commit 83fa0b4

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

integration/kotlinx-coroutines-slf4j/src/MDCContext.kt

+41-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,47 @@ public typealias MDCContextMap = Map<String, String>?
2828
* using [MDC.put]. These updates are going to be lost on the next suspension and
2929
* reinstalled to the MDC context that was captured or explicitly specified in
3030
* [contextMap] when this object was created on the next resumption.
31-
* Use `withContext(MDCContext()) { ... }` to capture updated map of MDC keys and values
32-
* for the specified block of code.
31+
*
32+
* For example, the following code will not work as expected:
33+
*
34+
* ```
35+
* launch(MDCContext()) {
36+
* MDC.put("key", "value") // This update will be lost
37+
* delay(100)
38+
* println(MDC.get("key")) // This will print null
39+
* }
40+
* ```
41+
*
42+
* Instead, you should use [withContext] to capture the updated MDC context:
43+
*
44+
* ```
45+
* launch(MDCContext()) {
46+
* MDC.put("key", "value") // This update will be captured
47+
* withContext(MDCContext()) {
48+
* delay(100)
49+
* println(MDC.get("key")) // This will print "value"
50+
* }
51+
* }
52+
* ```
53+
*
54+
* There is no way to implicitly propagate MDC context updates from inside the coroutine to the outer scope.
55+
* You have to capture the updated MDC context and restore it explicitly. For example:
56+
*
57+
* ```
58+
* MDC.put("a", "b")
59+
* val contextMap = withContext(MDCContext()) {
60+
* MDC.put("key", "value")
61+
* withContext(MDCContext()) {
62+
* MDC.put("key2", "value2")
63+
* withContext(MDCContext()) {
64+
* yield()
65+
* MDC.getCopyOfContextMap()
66+
* }
67+
* }
68+
* }
69+
* // contextMap contains: {"a"="b", "key"="value", "key2"="value2"}
70+
* MDC.setContextMap(contextMap)
71+
* ```
3372
*
3473
* @param contextMap the value of [MDC] context map.
3574
* Default value is the copy of the current thread's context map that is acquired via

integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt

+36
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,40 @@ class MDCContextTest : TestBase() {
105105
}
106106
}
107107
}
108+
109+
/** Tests that the initially captured MDC context gets restored after suspension. */
110+
@Test
111+
fun testSuspensionsUndoingMdcContextUpdates() = runTest {
112+
MDC.put("a", "b")
113+
withContext(MDCContext()) {
114+
MDC.put("key", "value")
115+
assertEquals("b", MDC.get("a"))
116+
yield()
117+
assertNull(MDC.get("key"))
118+
assertEquals("b", MDC.get("a"))
119+
}
120+
}
121+
122+
/** Tests capturing and restoring the MDC context. */
123+
@Test
124+
fun testRestoringMdcContext() = runTest {
125+
MDC.put("a", "b")
126+
val contextMap = withContext(MDCContext()) {
127+
MDC.put("key", "value")
128+
assertEquals("b", MDC.get("a"))
129+
withContext(MDCContext()) {
130+
assertEquals("value", MDC.get("key"))
131+
MDC.put("key2", "value2")
132+
assertEquals("value2", MDC.get("key2"))
133+
withContext(MDCContext()) {
134+
yield()
135+
MDC.getCopyOfContextMap()
136+
}
137+
}
138+
}
139+
MDC.setContextMap(contextMap)
140+
assertEquals("value2", MDC.get("key2"))
141+
assertEquals("value", MDC.get("key"))
142+
assertEquals("b", MDC.get("a"))
143+
}
108144
}

0 commit comments

Comments
 (0)