Scala Futures:每个新创建或映射异常的默认错误处理程序 [英] Scala Futures: Default error handler for every new created, or mapped exception

查看:63
本文介绍了Scala Futures:每个新创建或映射异常的默认错误处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否有可能始终使用默认的 onFailure 处理程序创建 Future{...} 块?(例如,将堆栈跟踪写入控制台)?此处理程序还应自动附加到映射的期货(通过在已经具有默认故障处理程序的未来上调用 map 创建的新期货)

Is there any possibility to always create a Future{...} block with an default onFailure handler? (e.g. write the stacktrace to the console)? This handler should also be automatically attached to mapped futures (new futures created by calling map on an future already having a default failure handler)

另见我的问题在这里了解更多详情:Android 上的 Scala 和 Scala.concurrent.Future 不报告系统 err/out 异常

See also my question here for more details: Scala on Android with scala.concurrent.Future do not report exception on system err/out

如果有人在返回的未来不使用 onFailure 或类似的东西,我想要一个最后的手段"异常记录代码.

I want to have a "last resort" exception logging code, if someone does not use onFailure or sth similar on a returned future.

推荐答案

我遇到了类似的问题,在实际结果不相关因而未明确处理的情况下,future 会默默地失败.根据 ExecutionContext 中的文档,我最初假设 reportFailure 方法用于报告 Future 中的任何故障.这显然是错误的 - 所以这是我想出的记录异常(即使是映射或以其他方式派生的)期货的方法:

I had a similar problem, futures failing silently in cases where the actual result is irrelevant and thus not handled explicitly. From the documentation in ExecutionContext I initially assumed that the reportFailure method there was to do reporting for any failure in a Future. Which is obviously wrong - so this is the approach I came up with to have logged exceptions (even for mapped or otherwise derived) futures:

  • 委托给 FutureonFailureLoggedFuture 类记录类似于@LimbSoups 答案的异常
  • 对于像 map 这样返回一个新的 Future 的方法也会产生一个 LoggedFuture
  • 使用 Promise 作为在级联 LoggedFutures 之间共享的某种失败事件,即使由于 onFailure 回调被多次应用,也只记录一次异常传播
  • a LoggedFuture class that delegates to a Future and onFailure logs the exception similar to @LimbSoups answer
  • for methods like map that return a new Future yield a LoggedFuture as well
  • use a Promise as some kind of fail event that is shared between the cascaded LoggedFutures to log an exception only once even if the onFailure callback is applied multiple times because of the propagation
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 这样的调用转换现有的期货.

Additionally, I have an implicit conversion to RichFuture (as above) defined so I can easily convert existing futures with calls like future.asLogged.

这篇关于Scala Futures:每个新创建或映射异常的默认错误处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆