concurrent.futures - Scala Futures: Default error handler for every new created, or mapped exception -
is there possibility create future{...} block default onfailure handler? (e.g. write stacktrace console)? handler should automatically attached mapped futures (new futures created calling map on future having default failure handler)
see question here more details: scala on android scala.concurrent.future not report exception on system err/out
i want have "last resort" exception logging code, if not use onfailure or sth similar on returned future.
i had similar problem, futures failing silently in cases actual result irrelevant , not handled explicitly. documentation in executioncontext
assumed reportfailure
method there reporting failure in future
. wrong - approach came have logged exceptions (even mapped or otherwise derived) futures:
- a
loggedfuture
class delegatesfuture
,onfailure
logs exception similar @limbsoups answer - for methods
map
return newfuture
yieldloggedfuture
well - use
promise
kind of fail event shared between cascadedloggedfutures
log exception once if onfailure callback applied multiple times because of propagation
object loggedfuture { def apply[t](future: future[t])(implicit ec: executioncontext): future[t] = { if (future.isinstanceof[loggedfuture[t]]) { // don't augment prevent double logging future.asinstanceof[loggedfuture[t]] } else { val failevent = promise[unit] failevent.future.onfailure { // 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 // promise used log error once, if // future mapped , 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) } 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 new future 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) }
additionally, have implicit conversion richfuture
(as above) defined can convert existing futures calls future.aslogged
.
Comments
Post a Comment