我遇到了类似的问题,在实际结果不相关且因此未明确处理的情况下,期货会默默地失败。从ExecutionContext
我最初的文档中假设该reportFailure
方法用于报告Future
. 这显然是错误的 - 所以这是我想出的记录异常(即使是映射或以其他方式派生的)期货的方法:
- 一个
LoggedFuture
委托给 aFuture
并onFailure
记录异常的类,类似于@LimbSoups 答案
- 对于这样的方法,也会
map
返回一个新的Future
产量 aLoggedFuture
- 使用 a
Promise
作为在级联之间共享的某种失败事件LoggedFutures
以仅记录一次异常,即使由于传播而多次应用 onFailure 回调
object LoggedFuture {
def apply[T](future: Future[T])(implicit ec: ExecutionContext): Future[T] = {
if (future.isInstanceOf[LoggedFuture[T]]) {
// don't augment to prevent double logging
future.asInstanceOf[LoggedFuture[T]]
}
else {
val failEvent = promise[Unit]
failEvent.future.onFailure {
// do your actual logging here
case t => t.printStackTrace()
}
new LoggedFuture(future, failEvent, ec)
}
}
}
private class LoggedFuture[T](future: Future[T], failEvent: Promise[Unit], ec: ExecutionContext) extends Future[T] {
// fire "log event" on failure
future.onFailure {
// complete log event promise
// the promise is used to log the error only once, even if the
// future is mapped and thus further callbacks attached
case t => failEvent.tryComplete(Failure(t))
} (ec)
// delegate methods
override def ready(atMost: Duration)(implicit permit: CanAwait): this.type = {
future.ready(atMost)
this
}
override def result(atMost: scala.concurrent.duration.Duration)(implicit permit: CanAwait): T = future.result(atMost)
override def isCompleted: Boolean = future.isCompleted
override def onComplete[U](func: scala.util.Try[T] => U)(implicit executor: ExecutionContext): Unit = future.onComplete(func)
override def value: Option[Try[T]] = future.value
// propagate LoggedFuture (and shared log event) whenever a new future is returned
override def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] =
new LoggedFuture(super.map(f), failEvent, executor)
override def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext): Future[S] =
new LoggedFuture(super.transform(s, f), failEvent, executor)
override def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] =
new LoggedFuture(super.flatMap(f), failEvent, executor)
override def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] =
new LoggedFuture(super.recover(pf), failEvent, executor)
override def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] =
new LoggedFuture(super.recoverWith(pf), failEvent, executor)
override def zip[U](that: Future[U]): Future[(T, U)] =
new LoggedFuture(super.zip(that), failEvent, ec)
override def fallbackTo[U >: T](that: Future[U]): Future[U] =
new LoggedFuture(super.fallbackTo(that), failEvent, ec)
override def andThen[U](pf: PartialFunction[Try[T], U])(implicit executor: ExecutionContext): Future[T] =
new LoggedFuture(super.andThen(pf), failEvent, executor)
}
class RichFuture[T](future: Future[T]) {
def asLogged(implicit ec: ExecutionContext): Future[T] = LoggedFuture(future)
}
此外,我RichFuture
定义了一个隐式转换为(如上),因此我可以轻松地使用future.asLogged
.