Stylianos Gakis
08/08/2022, 9:29 AMApplicationScope
like
class ApplicationScope(applicationScope: CoroutineScope) : CoroutineScope by applicationScope
// Then provided like
@Bean
fun applicationScope(): ApplicationScope {
return ApplicationScope(CoroutineScope(SupervisorJob() + <http://Dispatchers.IO|Dispatchers.IO>))
}
then converted an existing function to be fire-and-forget by using `launch`:
fun doSomething(...) = applicationScope.launch {...}
When an unhandled exception is thrown in the coroutine, somehow it doesn’t follow the normal logging/formatting process. Instead of a single error line in the log, we get each line in the stacktrace showing up as a separate line in the logs. This then shows up in the timeline as a whole lot of errors (50-100+) occurring at the same time.
Has someone who uses this combination of coroutines-slf4j-datadog experienced this before?
Could it be that the logger is not picking up these unhandled exceptions and it’s logging to the console? Or maybe we need to add something special to the ApplicationScope
to get SLF4J/Datadog to parse/format this error properly?dave
08/08/2022, 9:47 AMStylianos Gakis
08/08/2022, 10:11 AMCoroutineExceptionHandler { _, exception -> // log here }
inside our @Bean
providing the ApplicationScope
to simply log the exceptions ourselves there. This will come with the problem of not knowing where the error came from as simply as we do now with how we define our loggers per-class with something like private val logger: org.slf4j.Logger = LoggerFactory.getLogger(Foo::class.java)
. So unless there’s some way to get that information in there from the Throwable for example we probably have to skip this alternative.
2) We add a coroutineLogger pair everywhere where we do LoggerFactory.getLogger
with something like
// top level util
fun org.slf4j.Logger.coroutineExceptionHandlerLogger(): CoroutineExceptionHandler {
return CoroutineExceptionHandler { _, exception ->
error(exception)
}
}
// Per class Foo
class Foo(private val applicationScope: CoroutineScope = CoroutineScope(SupervisorJob())) {
private val logger: org.slf4j.Logger = LoggerFactory.getLogger(Foo::class.java)
private val coroutineLogger = logger.coroutineExceptionHandlerLogger()
fun foo() {
applicationScope.launch(coroutineLogger) {
}
}
}
And simply have to remember to never do a launch
without providing our extra CoroutineExceptionHandler in the context of those `launch`es.
Do you see some other alternative to this?