Skip to content

Commit

Permalink
Supply MDC context propagation with examples.
Browse files Browse the repository at this point in the history
Fixes #3280
  • Loading branch information
dkhalanskyjb committed Feb 12, 2024
1 parent 16ed1ef commit be88d19
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
43 changes: 41 additions & 2 deletions integration/kotlinx-coroutines-slf4j/src/MDCContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,47 @@ public typealias MDCContextMap = Map<String, String>?
* using [MDC.put]. These updates are going to be lost on the next suspension and
* reinstalled to the MDC context that was captured or explicitly specified in
* [contextMap] when this object was created on the next resumption.
* Use `withContext(MDCContext()) { ... }` to capture updated map of MDC keys and values
* for the specified block of code.
*
* For example, the following code will not work as expected:
*
* ```
* launch(MDCContext()) {
* MDC.put("key", "value") // This update will be lost
* delay(100)
* println(MDC.get("key")) // This will print null
* }
* ```
*
* Instead, you should use [withContext] to capture the updated MDC context:
*
* ```
* launch(MDCContext()) {
* MDC.put("key", "value") // This update will be captured
* withContext(MDCContext()) {
* delay(100)
* println(MDC.get("key")) // This will print "value"
* }
* }
* ```
*
* There is no way to implicitly propagate MDC context updates from inside the coroutine to the outer scope.
* You have to capture the updated MDC context and restore it explicitly. For example:
*
* ```
* MDC.put("a", "b")
* val contextMap = withContext(MDCContext()) {
* MDC.put("key", "value")
* withContext(MDCContext()) {
* MDC.put("key2", "value2")
* withContext(MDCContext()) {
* yield()
* MDC.getCopyOfContextMap()
* }
* }
* }
* // contextMap contains: {"a"="b", "key"="value", "key2"="value2"}
* MDC.setContextMap(contextMap)
* ```
*
* @param contextMap the value of [MDC] context map.
* Default value is the copy of the current thread's context map that is acquired via
Expand Down
36 changes: 36 additions & 0 deletions integration/kotlinx-coroutines-slf4j/test/MDCContextTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,40 @@ class MDCContextTest : TestBase() {
}
}
}

/** Tests that the initially captured MDC context gets restored after suspension. */
@Test
fun testSuspensionsUndoingMdcContextUpdates() = runTest {
MDC.put("a", "b")
withContext(MDCContext()) {
MDC.put("key", "value")
assertEquals("b", MDC.get("a"))
yield()
assertNull(MDC.get("key"))
assertEquals("b", MDC.get("a"))
}
}

/** Tests capturing and restoring the MDC context. */
@Test
fun testRestoringMdcContext() = runTest {
MDC.put("a", "b")
val contextMap = withContext(MDCContext()) {
MDC.put("key", "value")
assertEquals("b", MDC.get("a"))
withContext(MDCContext()) {
assertEquals("value", MDC.get("key"))
MDC.put("key2", "value2")
assertEquals("value2", MDC.get("key2"))
withContext(MDCContext()) {
yield()
MDC.getCopyOfContextMap()
}
}
}
MDC.setContextMap(contextMap)
assertEquals("value2", MDC.get("key2"))
assertEquals("value", MDC.get("key"))
assertEquals("b", MDC.get("a"))
}
}

0 comments on commit be88d19

Please sign in to comment.