Scala的Try
非常有用。
我想使用该模式,但记录所有异常。
我该怎么做?
答案 0 :(得分:25)
定义以下帮助程序:
import scala.util.{Try, Failure}
def LogTry[A](computation: => A): Try[A] = {
Try(computation) recoverWith {
case e: Throwable =>
log(e)
Failure(e)
}
}
然后您可以像使用Try
一样使用它,但任何异常都将通过log(e)
进行记录。
答案 1 :(得分:5)
您可以使用隐式类
进一步调整它def someMethod[A](f: => A): Try[A] = Try(f)
implicit class LogTry[A](res: Try[A]) {
def log() = res match {
case Success(s) => println("Success :) " + s); res
case Failure(f) => println("Failure :( " + f); res
}
}
现在,您可以调用someMethod
及其结果调用log
,如下所示:
scala> someMethod(1/0).log
Failure :( java.lang.ArithmeticException: / by zero
和
scala> someMethod(1).log
Success :) 1
当然,隐式类中的println
方法可以替换为您想要的任何日志记录。
答案 2 :(得分:1)
您使用的术语“例外”含糊不清。 (java.lang.)Throwable
是可以置于throw
术语后面的任何内容的根。 java.lang.Exception
是Throwable
的两个后代之一(另一个是java.lang.Error
)。进一步使这个含糊不清的是java.lang.RuntimeException
,Exception
的后代,这可能是您最常想要花费您的记录时间的地方(除非您正在执行更低级别的应用程序框架或硬件驱动程序实现)。
假设你想要字面上记录Throwable的所有实例,那么你需要这样的东西(不推荐):
def logAtThrowable(f: => A): Try[A] =
try
Try(f) match {
case failure @ Failure(throwable) =>
log(s"Failure: {throwable.getMessage}")
failure
case success @ _ =>
//uncomment out the next line if you want to also log Success-es
//log(s"Success: {throwable.getMessage}")
success
}
catch throwable: Throwable => {
//!NonFatal pathway
log(s"Failure: {throwable.getMessage}")
throw throwable
}
需要外部try/catch
来捕获Throwable
Try
/ {{1}内被scala.util.control.NonFatal
过滤掉的所有try
个实例阻止。
那就是说......有一个Java / JVM规则:你应该never define a catch clause at the resolution of Throwable(再次,除非你正在进行更低级别的应用程序框架或硬件驱动程序实现)。
遵循此规则的意图,您需要将catch
缩小到仅在更精细的级别上发出日志记录,比如Throwable
更精确一些。如果是这样,代码将如下所示(推荐):
java.lang.RuntimeException
在上面的两个代码段中,您会注意到我使用了def logAtRuntimeException(f: => A): Try[A] =
Try(f) match {
case failure @ Failure(throwable) =>
throwable match {
case runtimeException: RuntimeException =>
log(s"Failure: {runtimeException.getMessage}")
}
failure
case success @ _ =>
success
}
而不是match
。这有助于轻松添加有效的re .recoverWith
。事实证明,throw
上的所有方法本身也包含Try
/ try
块。这意味着,如果您要记录catch
,然后重新Throwable
,如果您使用throw
方法之一Try
,那么recoverWith
}会立即被捕获并放入throw
,从而完全破坏故意重新Failure
的价值。通过使用throw
,re match
可以保证成功,因为它在之外任何throw
方法。
如果你想在这个特定区域看到更多的兔子洞,我创建了一个blog post of my own exploration。
答案 3 :(得分:0)
从Scala 2.13
开始,链接操作tap
可用于在返回原始值的同时对任何值施加副作用(在这种情况下为某些日志记录):
import util.chaining._
val x = Try("aa".toInt).tap(_.failed.foreach(println))
// java.lang.NumberFormatException: For input string: "aa"
// x: Try[Int] = Failure(java.lang.NumberFormatException: For input string: "aa")
或等效的模式匹配版本:
val x = Try("aa".toInt).tap { case Failure(e) => println(e) case _ => }
// java.lang.NumberFormatException: For input string: "aa"
// x: Try[Int] = Failure(java.lang.NumberFormatException: For input string: "aa")
tap
链接操作对值(在这种情况下为println
)施加副作用(在这种情况下为Try
或某些日志记录),同时返回原始未修改的值, tap
已应用(Try
):
def tap [U](f:(A)=> U):A