Contains agent, which substitute in bytecode calls to future method wich accepts callbacks
( Future.apply'''
map,
flatMap
filter``` ... etc) to tracked versions which save origin caller stack.
Ie. tracked version collect stack trace of origin thread when appropriative construction is created and then, when handle exception, merge one with stack trace of this exception;
Useful for debugging.
-
publishLocal tracked-future to you local repository
-
when debug, enable agent
fork := true
javaOptions ++= Seq(
"--add-opens",
"java.base/java.lang=ALL-UNNAMED",
s"-javaagent:${System.getProperty("user.home")}/<repoDir>_<scalaVersion>/<version>/jars/trackedfuture_<scalaVersion>-assembly.jar"
)
where
- repoDir - location of you local repository
- scalaVersion - is a scala version as it used in artefacts suffixes
- version - version of trackedfuture
Use
- 0.5.0 version for scala 3
- 0.4.2 for scala 2.13.5
Let's look at the next code:
object Main
{
def main(args: Array[String]):Unit =
{
val f = f0("222")
try {
val r = Await.result(f,10 seconds)
} catch {
// will print with f0 when agent is enabled
case ex: Throwable => ex.printStackTrace
}
}
def f0(x:String): Future[Unit] =
{
System.err.print("f0:");
f1(x)
}
def f1(x: String): Future[Unit] =
Future{
throw new RuntimeException("AAA");
}
}
With tracked future agent enabled, instead traces, which ends in top-level executor:
f0:java.lang.RuntimeException: AAA
at trackedfuture.example.Main$$anonfun$f1$1.apply(Main.scala:30)
at trackedfuture.example.Main$$anonfun$f1$1.apply(Main.scala:30)
at trackedfuture.runtime.TrackedFuture$$anon$1.run(TrackedFuture.scala:21)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
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)
you will see traces wich include information: from where future was started:
f0:java.lang.RuntimeException: AAA
at trackedfuture.example.Main$$anonfun$f1$1.apply(Main.scala:30)
at trackedfuture.example.Main$$anonfun$f1$1.apply(Main.scala:30)
at trackedfuture.runtime.TrackedFuture$$anon$1.run(TrackedFuture.scala:21)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
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)
at java.lang.Thread.getStackTrace(Thread.java:1552)
at trackedfuture.runtime.TrackedFuture$.apply(TrackedFuture.scala:13)
at trackedfuture.runtime.TrackedFuture$.rapply(TrackedFuture.scala:39)
at trackedfuture.runtime.TrackedFuture.rapply(TrackedFuture.scala)
at trackedfuture.example.Main$.f1(Main.scala:29)
at trackedfuture.example.Main$.f0(Main.scala:25)
at trackedfuture.example.Main$.main(Main.scala:13)
at trackedfuture.example.Main.main(Main.scala)
If you want a version with more wrappend methods and with frames cleanup - don't hesitate to submit pull request ;)