Dariusz Kuc
09/09/2024, 9:03 PM1 MDC.put("k1", "v1")
2 <http://logger.info|logger.info>("one")
3 withContext(<http://Dispatchers.IO|Dispatchers.IO> + MDCContext()) {
4 MDC.put("k2", "v2")
5 <http://logger.info|logger.info>("two")
6 delay(10000) // Fake an API call
7 <http://logger.info|logger.info>("three")
8 }
9 <http://logger.info|logger.info>("four")
given the above I believe the behavior would be -
• when calling the function we got initial MDC value
• lines 1&2 will run on the same thread with updated MDC { "k1": "v1" }
• then at line 3 we start the coroutine which MAY change the thread
• our MDC context of this coroutine is { "k1": "v1" }
• 4 & 5 will run on the same thread with modified MDC { "k1": "v1", "k2": "v2" }
• 6 is suspendable call and once it returns it may resume on a different thread
• 7 resets the MDC back to { "k1": "v1" }
(from line 3)
• we are back to main logic where we may run on a different thread which then uses MDC context from when we entered the function (i.e. initial MDC)
and yes in order to have consistent logging behavior you would have to restore the MDC after each suspension point
see the docs -> https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-slf4j/kotlinx.coroutines.slf4j/-m-d-c-context/Owen Hope
09/09/2024, 9:11 PMThere 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.but based on that example I had assumed the MDCContext with
{ "k1": "v1" }
would have still existed in line 9 since that was not coming from an update inside the coroutine on line 3
Is it a common practice then to add a helper method to restore the MDC after each suspension point? Such as something like
suspend fun withMDCContext(val dispatcher: Dispatcher) {
val contextMap = MDC.getCopyOfContextMap()
withContext(dispatcher + MDCContext()) {
// API call
}
MDC.setContextMap(contextMap)
}
Or by doing this in a suspend
fun do I still run the risk of the MDCContext being lost?Dariusz Kuc
09/09/2024, 11:41 PMDariusz Kuc
09/09/2024, 11:42 PMDariusz Kuc
09/09/2024, 11:47 PMwithContext
▪︎ [3] invoke another suspendable function (delay
)
â—¦ when you return after calling the delay you get context back from [2] (i.e. MDC with k1 only)
• when you exit out of withContext
you get the context back from [1] (thats why you dont get those extra MDC values there and you only get whatever was set in the context factory)Owen Hope
09/10/2024, 1:22 PM