inheritLoggingContext

Wraps an ExecutorService in a new implementation that copies logging context fields (from withLoggingContext) from the parent thread to child threads when spawning new tasks. This is useful when you use an ExecutorService in the scope of a logging context, and you want the fields from the logging context to also be included on the logs in the child tasks.

Example

Scenario: We store an updated order in a database, and then want to asynchronously update statistics for the order.

import dev.hermannm.devlog.field
import dev.hermannm.devlog.getLogger
import dev.hermannm.devlog.inheritLoggingContext
import dev.hermannm.devlog.withLoggingContext
import java.util.concurrent.Executors

private val log = getLogger()

class OrderService(
private val orderRepository: OrderRepository,
private val statisticsService: StatisticsService,
) {
// Call inheritLoggingContext on the executor
private val executor = Executors.newSingleThreadExecutor().inheritLoggingContext()

fun updateOrder(order: Order) {
withLoggingContext(field("order", order)) {
orderRepository.update(order)
updateStatistics(order)
}
}

// In this scenario, we don't want updateStatistics to block updateOrder, so we use an
// ExecutorService to spawn a thread.
//
// But we want to log if it fails, and include the logging context from the parent thread.
// This is where inheritLoggingContext comes in.
private fun updateStatistics(order: Order) {
executor.execute {
try {
statisticsService.orderUpdated(order)
} catch (e: Exception) {
// This log will get the "order" field from the parent logging context
log.error(e) { "Failed to update order statistics" }
}
}
}
}