In the following code:
object Xxx extends App {
def makeFuture: Future[String] = Future {
throw new Exception("An exception")
}
def main() {
val anotherFuture = makeFuture map { s => s + " extended string " }
anotherFuture.onComplete {
case Success(s) => println(s)
case Failure(t) => t.printStackTrace()
}
Thread.sleep(1000)
}
main()
}
I obtain the meaningless stacktrace:
java.lang.Exception: An exception
at Xxx$$anonfun$makeFuture$1.apply(Xxx.scala:10)
at Xxx$$anonfun$makeFuture$1.apply(Xxx.scala:10)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Something similar happens in this case:
object Xxx extends App {
def makeFuture: Future[String] = Future {
"Some string"
}
def main() {
val anotherFuture = makeFuture map { s => throw new Exception("An exception") }
anotherFuture.onComplete {
case Success(s) => println(s)
case Failure(t) => t.printStackTrace()
}
Thread.sleep(1000)
}
main()
}
getting this stacktrace:
java.lang.Exception: An exception
at Xxx$$anonfun$1.apply(Xxx.scala:14)
at Xxx$$anonfun$1.apply(Xxx.scala:14)
at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:253)
at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:249)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:29)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
I'd like to obtain more data from these exceptions. For instance, in the first case, I can tell it was a Future made in the "makeFuture" method, but I'd like to know that I called this method from my "main" method.
In the latter case all I know is that it's a future map in my Xxx class.
Of course this are simple cases, but I'm dealing with tenths of Future calls that are composed together (using fallbackTo, Future.sequence, etc). So it's hard for me to reason about what happened when I have to deal with future failures.
I know how future works, execution contexts and runnable blocks, but that doesn't make my issue easier. My ideal would be to have a stack trace snapshot when the Promise instance is created, and attach it to the exception failure. In the meantime, are there any best practices that I can use to deal with these situations?
(For the record, most of my future failures come from Spray client, or Akka ask timeouts, but it would be the same for any kind of future failure).
Thanks!