sentry-java icon indicating copy to clipboard operation
sentry-java copied to clipboard

Support SentryCoroutineExceptionHandler

Open marandaneto opened this issue 4 years ago • 7 comments

As we have a sentry-kotlin-extensions module right now and SentryContext for Coroutines, it'd be a nice addition offering a SentryCoroutineExceptionHandler, https://kotlinlang.org/docs/exception-handling.html#coroutineexceptionhandler so people can use it and when it throws, it captures an event automatically, how does that sound? Similar to our UncaughtExceptionHandlerIntegration

marandaneto avatar Jun 11 '21 07:06 marandaneto

val handler = SentryCoroutineExceptionHandler { _, exception -> 
    // ideally we create an event with a mechanism
    Sentry.captureException(exception)
}
val job = GlobalScope.launch(handler) {
    throw AssertionError()
}

marandaneto avatar Jun 11 '21 07:06 marandaneto

examples can be found on https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-core/jvm/test/guide example-exceptions-{number}-kt files

marandaneto avatar Jun 11 '21 07:06 marandaneto

works like a try/catch, if no CoroutineExceptionHandler is given, UncaughtExceptionHandlerIntegration gets called and crashes the App, but if a CoroutineExceptionHandler is given, it gets caught and swallowed, so its a bit less boilerplate than a try/catch but works the same way

marandaneto avatar Jun 11 '21 07:06 marandaneto

one thing to note is:

[CoroutineExceptionHandler] can be invoked from an arbitrary thread

so we cannot use the current thread when creating a ExceptionMechanismException

marandaneto avatar Jun 11 '21 08:06 marandaneto

public object SentryCoroutineExceptionHandler : AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
    override fun handleException(context: CoroutineContext, exception: Throwable) {
        val mechanism = Mechanism().apply {
            // null and true is the same
            // isHandled = true
            type = "CoroutineExceptionHandler"
        }
        // cannot use the Thread.currentThread()
        // [CoroutineExceptionHandler] can be invoked from an arbitrary thread
        // ExceptionMechanismException should have an overload without a thread
        val error = ExceptionMechanismException(mechanism, exception, Thread.currentThread())
        val event = SentryEvent(error)
       // we probably need a hint here to not append anything from the `currentThread`
        Sentry.captureEvent(event)
    }
}
GlobalScope.launch(SentryCoroutineExceptionHandler) {
    throw AssertionError()
}
Screenshot 2021-06-11 at 10 31 16

marandaneto avatar Jun 11 '21 08:06 marandaneto

related topic https://kt.academy/article/cc-exception-handling

marandaneto avatar Jan 09 '22 23:01 marandaneto

Consider testing https://github.com/Anamorphosee/stacktrace-decoroutinator and adding to the docs if it gives better stack traces

marandaneto avatar May 24 '22 07:05 marandaneto

Relates to https://github.com/getsentry/sentry-java/issues/2687 as well

marandaneto avatar Jun 01 '23 13:06 marandaneto