2014-06-27 12 views
4

是否有可能永远创造未来{...}用默认onFailure处处理阻止? (例如,将堆栈跟踪写入控制台)?此处理也应该自动连接到映射期货(通过调用地图上的未来已经有一个默认的故障处理程序创建新期货)斯卡拉期货:默认错误处理程序,为每一个新创建的,或映射异常

另见我的问题在这里了解更多详情: Scala on Android with scala.concurrent.Future do not report exception on system err/out

我想有“最后手段”异常日志记录代码,如果有人不使用onFailure处或某物类似上返回的未来。

+0

你没有明白了,没有必要为每个映射的未来做出失败回调,因为在失败的情况下map不会进行任何计算,只是进一步通过现有的失败。所以如果你将更多的计算链接到失败的计算,所有新的回调就不会被调用。 – 4lex1v

+0

感谢AlexIv,我知道,但这不是我的问题,请在 – longliveenduro

回答

3

我有一个类似的问题,在实际结果不相关,因此没有明确处理的情况下,期货会悄无声息地失败。从ExecutionContext的文档中,我最初认为reportFailure方法需要报告Future中的任何故障。这显然是错误的 - 所以这是我想出了已经登录异常(甚至映射或以其他方式得到的)期货的办法:

  • 一个LoggedFuture类委托给FutureonFailure记录类似于@异常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呼叫。

+0

我没有在您的示例中看到任何隐式转换。 ?? – sweepy84

+0

@ sweepy84代码只包含类,隐式转换在其他地方定义(我在包对象中做过 - 需要导入),看起来像这样:'implicit def future2RichFuture [T](future:Future [T ])= new RichFuture(future)' – stempler

+0

隐式def可以通过将'RichFuture'设为隐式类来避免: '隐式类RichFuture [+ T](val future:Future [T])扩展AnyVal def asLogged (隐含的ec:ExecutionContext):Future [T] = LoggedFuture(未来) } – Bruno

0

就像一个延伸到我的评论:

你没有得到的地步,还有在地图失败的情况下,使每一个映射未来失败的回调,原因没有必要也不会做任何计算,只进一步传递现有失败。所以如果你将更多的计算链接到失败的计算,所有新的回调就不会被调用。

考虑这个例子:

case class TestError(msg) extends Throwable(msg) 

val f1 = Future { 10/0 } 
val f2 = f1 map { x => throw new TestError("Hello"); x + 10 } 
f1.onFailure { 
    case error => println(error.getMessage) 
} 
f2.onFailure { 
    case er: TestError => println("TestError") 
    case _ => println("Number error") 
} 

// Exiting paste mode, now interpreting. 

/by zero 
Number error 
f1: scala.concurrent.Future[Int] = [email protected] 
f2: scala.concurrent.Future[Int] = [email protected] 

正如你所看到的第一个回调打印错误消息,并抛出TestError第二忽略。那是因为你的地图功能不适用。如果你看看到map评论:

/** Creates a new future by applying a function to the successful result of 
* this future. If this future is completed with an exception then the new 
* future will also contain this exception. 
*/ 

所以在安装新的故障的回调进一步不用,造成任何进一步的未来将简单的包含前一个的结果,对于每个你已经已经定义了一个回调。

+0

以下回答我的意见,谢谢AlexIv,我已经明白了。我一直要求的仅仅是一个简单的方法来总是使用期望与给定的失败处理程序。当我使用map()例如我必须重新添加处理器,以处理map-function中可能出现的错误。 – longliveenduro

+0

@longliveenduro再次,不,这是你没有得到的观点。看看我的例子,它在'map'内部抛出'TestError',但是如果'f1'包含失败,'f2'将不包含'TestError',而是'NumberError',所以不需要附加更多的失败处理程序进一步的期货,导致他们不会发生 – 4lex1v

+0

是的,你说的是绝对正确的,但不幸的是没有解决我问过的问题。我在这里发现了一个非常类似的讨论:http://grokbase.com/t/gg/scala-user/1419gxgzza/the-silence-of-failed-futures – longliveenduro

1

用下面的隐含类,你可以轻松地记录你的期货失败,同时避免recover样板:

import com.typesafe.scalalogging.Logger 

    implicit class LoggingFuture[+T](val f: Future[T]) extends AnyVal { 
    def withFailureLogging(l: Logger, message: String): Future[T] = f recover { 
     case e => 
     l.error(s"$message: $e") 
     throw e 
    } 

    def withPrintStackTraceOnFailure: Future[T] = f recover { 
     case e => 
     e.printStackTrace() 
     throw e 
     } 
    } 

您可以使用它,如下图所示:

import com.typesafe.scalalogging._ 
import scala.language.postfixOps 

class MyClass extends LazyLogging { 
    def f = Future { 
    // do something that fails 
    throw new Exception("this future fails") 
    } withFailureLogging(logger, "some error message") 

    def g = Future { 
    // do something that fails 
    throw new Exception("this future fails") 
    } withPrintStackTraceOnFailure 
}