Scala 尝试使用异常日志记录
Scala的Scala 尝试使用异常日志记录,scala,exception,logging,Scala,Exception,Logging,Scala的Try非常有用 我希望使用该模式,但记录所有异常 如何执行此操作?定义以下帮助程序: import scala.util.{Try, Failure} def LogTry[A](computation: => A): Try[A] = { Try(computation) recoverWith { case e: Throwable => log(e) Failure(e) } } 然后您可以像使用Try一样使用它,但是任何
Try
非常有用
我希望使用该模式,但记录所有异常
如何执行此操作?定义以下帮助程序:
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)
记录。您可以使用隐式类进一步调整它
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
及
当然,隐式类中的println
方法可以替换为您想要的任何日志记录 您使用的术语“例外情况”模棱两可。是可以放置在抛出
术语后面的任何内容的根。是(另一种生物)的两个后代之一。使这一点更加模棱两可的是,异常
的后代,这可能是您最想花费日志时间的地方(除非您正在执行较低级别的应用程序框架或硬件驱动程序实现)
假设您希望逐字记录所有Throwable实例,那么您将需要以下内容(不推荐):
外部try/catch
需要捕获try
的try
/catch
块中过滤掉的所有Throwable
实例
也就是说,有一条Java/JVM规则:您应该(同样,除非您正在执行较低级别的应用程序框架或硬件驱动程序实现)
按照这条规则的意图,您需要将可丢弃的
范围缩小到只在更细粒度级别发出日志,比如说更精细的java.lang.RuntimeException
。如果是这样,代码将如下所示(推荐):
在上面的两段代码中,您会注意到我使用了match
,而不是.recoverWith
。这有助于轻松添加一个有效的rethrow
。事实证明,Try
上的所有方法本身也用Try
/catch
块包装。这意味着,如果您想记录可丢弃的文件,然后重新抛出它,如果您使用的是方法之一,请尝试recoverWith
,重新抛出
会立即被重新考虑,并被置于失败
状态,从而完全破坏故意重新抛出
的价值。通过使用match
,可以保证重新抛出
成功,因为它不在任何Try
方法的范围内
如果您想在这个特定区域周围看到更多的兔子洞,我创建了一个。开始Scala 2.13
,链接操作可用于在返回原始值的同时对任何值应用副作用(在本例中是一些日志记录):
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")
链接操作对值(在本例中为aTry
)应用了副作用(在本例中为println
或某些日志记录),同时返回应用了tap
的原始未修改值(在Try
):
def tap[U](f:(A)=>U):A
我会试试的。顺便说一句,为了匹配Try源代码,它不应该只捕获非致命异常吗?@SRobertJames-不,因为它已经只捕获了非致命异常,所以只有那些异常需要恢复。除非您的意思是:我可以记录Try的所有异常,包括它无法捕获的异常吗?那么答案是否定的;您必须插入自己的自定义日志代码。如果禁用了可丢弃的功能,则此解决方案不会发出日志!非致命。您可能不希望在log
上有类型参数A
,因为它会隐藏LogTry
的A
。您也可以只返回res
,而不返回创建新实例的Success(s)
和Failure(f)
。我稍后会回来写一个正式答案。然而,这里有一篇博文,我刚刚完成了对这个问题(以及其他相关问题)的讨论:
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
}
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")