Coroutines logging names


#1

Hello,

I’m using kotlin + vert.x and I’m having hundreds of coroutines running simultaneously. Now, I’m trying to differentiate logs on per coroutine basis.
What I’ve found so far is kotlin coroutines debug that enumerates every coroutine and adds this number to Thread.currentThread().name.
After this, when my coroutines using log4j2 logger theirs name is being calculated by retrieving coroutine name part from thread name (e.g. thread name “vertx-common-pool-7 @coroutine-10” will produce the name of the coroutine “coroutine-10”).

It almost suits my needs by I got several questions:

  1. What’s the overhead of using coroutines debug? Does the performance of the app going to suffer from this?
  2. Is it possible to do naming myself (instead of using coroutines debug)? I’ve tried adding CoroutineName element to CoroutineContext but it’s not very straightforward
  3. I’ve also tried modifying Thread.name when launching new coroutines but this gets messy somehow (it ends up with multiple coroutines having the same names and number of different names equals to the number of event loops somehow).

Are there any recommendations to achieve the result of naming every coroutine for logging purposes?


#2

OK, I’ve found a solution how you can add custom name to your coroutine without the overhead of using coroutines debug. As an example, I’ve taken debug functionality for enumerating coroutines and here’s what I got:

private const val COROUTINE_NAME_SEPARATOR = " @"

data class LoggableCoroutineName(val name: String) : ThreadContextElement<String>, AbstractCoroutineContextElement(LoggableCoroutineName) {
    companion object Key : CoroutineContext.Key<LoggableCoroutineName>
    override fun toString(): String = "CoroutineName($name)"
    override fun updateThreadContext(context: CoroutineContext): String {
        val coroutineName = context[LoggableCoroutineName]?.name ?: "default-coroutine"
        val currentThread = Thread.currentThread()
        val oldName = currentThread.name
        var lastIndex = oldName.lastIndexOf(COROUTINE_NAME_SEPARATOR)
        if (lastIndex < 0) lastIndex = oldName.length
        currentThread.name = buildString(lastIndex + coroutineName.length + 5) {
            append(oldName.substring(0, lastIndex))
            append(COROUTINE_NAME_SEPARATOR)
            append(coroutineName)
        }
        return oldName
    }
    override fun restoreThreadContext(context: CoroutineContext, oldState: String) {
        Thread.currentThread().name = oldState
    }
}

Now, when you want to launch a new Named coroutine you can do this:

launch(LoggableCoroutineName(coroutineName)) {
    // Your code here
}

Like this, your thread name will contain coroutine name and you will be able to parse it for logging purposes.