Hello, is there a more idiomatic way to catch all...
# coroutines
p
Hello, is there a more idiomatic way to catch all errors but
CancellationException
? Cancelling the job is a normal operation that is not considered as an error. Code snippet:
Copy code
job = scope.launch {
                    try {
                         // proccess the action
                    } catch (e: CancellationException) {
                        throw e
                    } catch (e: Throwable) {
                        // log the error
                    }
 }
r
how’s this?
Copy code
job = scope.launch {
                    try {
                         // proccess the action
                    } catch (e: Throwable) {
                        coroutineContext.ensureActive()
                        // log the error
                    }
 }
m
Wouldn’t the stack trace of the initial exception be lost?
What about a one-line check in the catch block? It’s still repetitive but at least you won’t need two catch blocks.
Copy code
if (e is CancellationException) throw e
Or make it an extension function:
Copy code
e.rethrowCancellation()
p
The thing is that when we onboard new developers that are not familiar with coroutines they forget/do not know yet to bubble the
CancellationException
when catching all errors. I see a lot of
Job was cancelled
in our kibana logs which are just job cancellations not real errors so I was wondering is there a different approach.
r
@Marc Knaup hmm, in the case that a CancellationException was not thrown and by the time you call
ensureActive()
the coroutine is no longer active then yes, you’re correct. Is that possible?
m
Would be nice if
CancellationException
would just be a
Throwable
and catch-all is just
e: Exception
. Catching
Throwable
is still evil, isn’t it? 😄
p
It is but it's a worker function that needs to keep processing other actions event if one fails.
m
Sure it does. But non-exceptions are usually
Error
and that means something is REALLY wrong (like
OutOfMemoryError
). I’d rather restart the server then and fix asap than trying to keep a potentially unstable JVM alive.
You could also be very hacky and make your logging function rethrow
CancellationException
instead of logging it 😂
.logIfNotCancel
🙂
Or actually if it’s just about logging then make it a reusable functionality! No need to always wrap all
launch
code into
try … catch (Throwable)
scope.launchWithExceptionLogging { … }
I think it can be done even without a custom extension by installing an exception handler in your coroutine context(s).
g
I prefer explicit exception management with try/catch instead of exceptionhandler
👆 1
m
Even if it’s just for the purpose of logging all exceptions except one kind? Possibly across hundreds of call-sites? 🤔
g
Depends on use case, if it's just simple launch + try/catch I would just write extension function. Also, to use exception handler you have to add to all contexts, also it doesn't work for non-supervisor job. So I don't see how it would be better than explicit logging, which also more flexible (you want just log and rethrow, or you want log and return default value or just ignore and cancel)