我有一个类似的问题,在实际结果不相关,因此没有明确处理的情况下,期货会悄无声息地失败。从ExecutionContext
的文档中,我最初认为reportFailure
方法需要报告Future
中的任何故障。这显然是错误的 - 所以这是我想出了已经登录异常(甚至映射或以其他方式得到的)期货的办法:
- 一个
LoggedFuture
类委托给Future
和onFailure
记录类似于@异常LimbSoups回答
- 像
map
返回一个新Future
收益率方法LoggedFuture
以及
- 使用
Promise
为某种失败的事件被级联LoggedFutures
之间共享记录异常仅即使onFailure处回调是一次APPLI编因为传播
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
呼叫。
你没有明白了,没有必要为每个映射的未来做出失败回调,因为在失败的情况下map不会进行任何计算,只是进一步通过现有的失败。所以如果你将更多的计算链接到失败的计算,所有新的回调就不会被调用。 – 4lex1v
感谢AlexIv,我知道,但这不是我的问题,请在 – longliveenduro