Попробуйте с протоколированием исключений

#scala #exception #logging

#scala #исключение #протоколирование

Вопрос:

Scala Try очень полезен.

Я бы хотел использовать этот шаблон, но регистрировать все исключения.

Как я могу это сделать?

Комментарии:

1. Я вернусь и напишу официальный ответ позже. Однако вот сообщение в блоге, которое я только что закончил рассматривать только эту проблему (и другие связанные с ней): fromjavatoscala.blogspot.com/2016/09 /…

Ответ №1:

Определите следующего помощника:

 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. Попробую. Кстати, для сопоставления попробуйте исходный код github.com/scala/scala/blob/v2.11.1/src/library/scala/util /… , разве он не должен улавливать только несмертельные исключения?

2. @SRobertJames — Нет, потому что он уже поймал только несмертельные исключения, так что есть только те, от которых нужно оправиться. Если вы не имеете в виду: могу ли я регистрировать все исключения из Try, включая те, которые он не может перехватить ? Тогда ответ будет отрицательным; вы должны вставить свой собственный пользовательский код ведения журнала.

3. Это решение не будет выдавать протоколирование, если Throwable есть! Нефатальный.

Ответ №2:

Начиная Scala 2.13 , операция цепочки U):A» rel=»noreferrer»> 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")
  

Операция U):A» rel=»noreferrer»> tap цепочки применяет побочный эффект (в данном случае println или некоторое протоколирование) к значению (в данном случае a Try ), возвращая исходное неизмененное значение, к которому tap применяется (the Try ):

def tap[U](f: (A) => U): A

Ответ №3:

Вы можете настроить его еще больше, используя неявный класс

 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 , метод внутри неявного класса можно заменить любым протоколированием, которое вы хотите.

Комментарии:

1. Вы, вероятно, не хотите, чтобы параметр типа A был включен log , поскольку он затеняет A LogTry .

2. Вы также можете просто вернуть res вместо возврата Success(s) и Failure(f) , которые создают новые экземпляры.

Ответ №4:

Вы использовали термин «исключения», который является неоднозначным. (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 экземпляров, которые отфильтровываются scala.util.control.NonFatal внутри блока Try ‘s try / catch .

Тем не менее … существует правило Java / JVM: вы никогда не должны определять предложение catch при разрешении Throwable (опять же, если вы не используете низкоуровневую платформу приложений или аппаратные реализации драйверов).

Следуя замыслу этого правила, вам нужно будет сузить круг 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 вместо .recoverWith . Это делается для облегчения простого добавления re throw , который работает. Оказывается, что все методы сами по Try себе также обернуты try / catch blocks . Это означает, что если вы хотите зарегистрировать Throwable , а затем повторно throw , если вы используете один из Try методов, подобных recoverWith , re throw немедленно перехвачен и помещен в a Failure , тем самым полностью подрывая значение преднамеренного re throw . При использовании match re throw гарантируется успех, поскольку он остается вне любого из Try методов.

Если вы хотите увидеть больше кроличьих нор вокруг этой конкретной области, я создал сообщение в блоге о своем собственном исследовании.