Skip to content

Easier construction of MDCContext #4600

@schielek

Description

@schielek

Use case

Using the MDCContext of the SLF4J extension is cumbersome. It is recommended to use it in the following way:

MDC.put("a", "1")
MDC.put("b", "2")
MDC.put("c", "3")
withContext(MDCContext()) {
    // ...
}

The code has two problems:

  1. Many lines required for something that could fit on one line
  2. The MDC values are leaking into code after the withContext block. On backend server applications where threads are reused they could leak into other HTTP calls.

Cleaning up the MDC adds even more boilerplate code:

val closables = listOf(
    MDC.putCloseable("a", "1"),
    MDC.putCloseable("b", "2"),
    MDC.putCloseable("c", "3"),
)
try {
    withContext(MDCContext()) {
        // ...
    }
} finally {
    closables.forEach { it.close() }
}

The Shape of the API

It would be nice to have a secondary constructor, which takes key values pairs as arguments so I can just write:

withContext(MDCContext("a" to "1", "b" to "2", "c" to "3")) {
    // ...
}

As a current workaround I use the following function, but I think a secondary constructor as part of the library would be better:

fun MDCContext(vararg keyValuePairs: Pair<String, String>) = MDCContext(
    (MDC.getCopyOfContextMap() ?: emptyMap()) + keyValuePairs,
)

This offers a concise way of setting up values in the MDC without polluting the current thread. Also, it behaves better than the example from above: The MDC is not only cleaned up, but rather restored to its previous state after the withContext block.

You can find my PR here.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions