尝试异常日志记录

时间:2014-06-24 19:00:07

标签: scala exception logging

Scala的Try非常有用。

我想使用该模式,但记录所有异常。

我该怎么做?

4 个答案:

答案 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.ExceptionThrowable的两个后代之一(另一个是java.lang.Error)。进一步使这个含糊不清的是java.lang.RuntimeExceptionException的后代,这可能是您最常想要花费您的记录时间的地方(除非您正在执行更低级别的应用程序框架或硬件驱动程序实现)。

假设你想要字面上记录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