2

While working with CompletableFuture in spring boot application, I noticed that there was an exception occurring in function which is ran Async using CompletableFuture, but there was nothing logs. The function execution stopped and hence CompletableFuture was completed. Why was there nothing in logs? When I used .whenComplete with .runAsync, I was able to log the error.

void function() {
   CompletableFuture
     .runAsync(() -> runAsyncfunction())
     .whenComplete((result, err) -> {//log the error})
 }

EDIT: If I don't use the .whenComplete the JVM doens't print the stack trace of uncaught exception occuring in runAsyncfunction automatically. But If I use .whenComplete I can log the exception.

Also, is it good industry practice to use .whenComplete in such scenario ?

Java Programmer
  • 179
  • 1
  • 12
  • 2
    The exception will be on the CompletableFuture if that async method does not handle it. Does that async method catch and log? Please show some code. – Andrew S Nov 04 '21 at 12:45
  • It's hard to tell what's going on with just that description. Some code that shows what you're doing would help, e.g. some portion of your code could actually catch and swallow the exception. – Thomas Nov 04 '21 at 12:48
  • 1
    Completable future does not inherently catch or rethrow exceptions that occur inside of its async Runnable. Uncaught exceptions will cause strange behavior like frozen future execution like you describe here. – Jake Henry Nov 04 '21 at 12:56
  • Where do you log the exception? – knittl Nov 04 '21 at 16:51
  • 1
    CompltableFuture doesn't "log" anything, that's your code's job. Handling exceptions correctly requires that the async code catches exceptions and completes the future exceptionally. The code waiting on the future needs to arrange that it knows when the future has completed, whether normally or exceptionally. – user16632363 Nov 04 '21 at 16:52
  • @AndrewS Please check the code now – Java Programmer Nov 04 '21 at 19:25
  • @Thomas please see the code and edit changes – Java Programmer Nov 04 '21 at 19:26
  • @user16632363 Yes, I meant JVM doesn't print stacktrace of the exception like it should. – Java Programmer Nov 04 '21 at 19:27
  • No, it "shouldn't". See my answer. – user16632363 Nov 04 '21 at 22:26
  • Does this answer your question? [CompletableFuture not working as expected](https://stackoverflow.com/questions/53774471/completablefuture-not-working-as-expected) – Didier L Nov 04 '21 at 22:38

1 Answers1

3

"The JVM" does not print exception stack traces of its own free will. You need to write code that makes it happen. About the only case I can think of where it might appear that happens is if an exception escapes out of main(). Otherwise, code you wrote or code you called has explicit instructions to print out a stack trace. Maybe if your code is called from some Spring Boot code, the Spring Boot code has an exception handler to do that for you - I don't know, I don't use it.

In this case

someFuture.runAsync(() -> runAsyncfunction());

at the time you call someFuture.runAsync(), you will be returned a new CompletableFuture, which you have ignored.

When someFuture completes, your runAsyncFunction() will execute, likely in some other thread. If that throws an exception, the 'future that you ignored' will complete exceptionally. But you'll never find out about it, because you ignored the mechanism that would have told you.

To summarize: some exception was thrown in some other thread, the thread running the async routine. That exception was caught there, and used to "complete the future exceptionally". But you need to do something to find out that the future was completed; it's just unexamined status information in the future until you do.

This construction:

someFuture.runAsync(() -> runAsyncfunction())
          .whenComplete(blah blah);

is equivalent to

CompletableFuture<Void> x =
    someFuture.runAsync(() -> runAsyncfunction());
x.whenComplete(blah blah);

Perhaps that makes it clearer what is going on in that case. 'runAsync' fails, 'x' is completed exceptionally. The whenComplete then fires, and you can do something useful with the failure.


Footnote: the code as posted looks invalid to me.

CompletableFuture
   .runAsync(() -> runAsyncfunction())
   .whenComplete((result, err) -> {//log the error})

so I hope I divined your intent correctly.

user16632363
  • 1,050
  • 3
  • 6