Как получить stackTrace от абонента - PullRequest
0 голосов
/ 28 декабря 2018

Я хочу иметь возможность отображать в журнале, кто вызвал ошибку:

def printError(errorMessage: String): Error = {
   new Error(errorMessage, /* create stack trace */)
}

def badMethod(...): IO[Error, ...] = {
    ...
    IO.fail(printError("This is bad"))
}

Я хочу что-то внутри printError, которое показывало бы, что ошибка была создана в badMethod.

Я пытался с Thread.getAllStackTraces.get(Thread.currentThread()).toList, но я получаю заголовок стека в printError.

В модульном тестировании есть класс Position, который может это сделать, но я не знаю, как использовать его в контексте без тестирования.

Может быть, некоторые implicit что-нибудь сделают?

Есть идеи?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Я не думаю, что есть действительно хорошее решение этой проблемы.Я думаю, что вы должны выбрать один из следующих вариантов, каждый из которых имеет свои недостатки:

  1. Live с заголовком стека printError.Я не уверен, действительно ли это плохой вариант.Между прочим, существует простой вызов Thread.getStackTrace(), который можно использовать, как в Thread.currentThread.getStackTrace()

  2. Добавить явный аргумент printError

  3. Попытайтесь взломать возвращенный StackTraceElement[], чтобы удалить некоторые верхние элементы, которые вы не хотите видеть.Помните, что это может быть не так просто, потому что потенциально компилятор может встроить некоторые вызовы методов.

  4. Создайте собственное решение на основе макросов, например, ScalaTest Position .Я не думаю, что вы можете сделать это без использования макроса, хотя.С другой стороны, макрос не должен быть сложным, потому что у макросов Context есть свойство enclosingPosition.Но имейте в виду, что макросы являются одной из наименее стабильных частей Scala, поэтому они могут быть не очень перспективными.

  5. Непосредственно используйте ScalaTest Position, который былперенесен в самостоятельный проект Scalactic , который вы можете использовать в своем рабочем коде.Это похоже на # 4, но вы перекладываете бремя обслуживания вашего решения на ребят из ScalaTest.

Недостаток # 4 и # 5 заключается в том, что вы получаете только исходную позициюbadMethod вместо трассировки всего стека, что ИМХО намного хуже для отладки проблем.

Объедините # 3 и # 4, чтобы создать макрос, который создаст значение обертки над Thread.currentThread.getStackTrace() вместо Context.enclosingPosition и передаст его в качестве вашей "позиции".

Обновлениена # 6

На самом деле реализация # 6 не так сложна, если у вас уже есть макросы в вашем проекте, и вы можете использовать квазицитаты .Вот простой PoC:

Макропроект

package so.stacktrace
import scala.language.experimental.macros

class StackTraceWrapper(t: Throwable) {
  lazy val stackTrace: List[String] = t.getStackTrace().toList.map(_.toString)
}

object StackTraceWrapper {
  implicit def currentStackTrace: StackTraceWrapper = macro StackTraceWrapperMacro.buildCurrent
}


object StackTraceWrapperMacro {

  import scala.reflect.macros.Context

  def buildCurrent(context: Context) = {
    import context.universe._
    // note that package here should match the actual package!
    q"new so.stacktrace.StackTraceWrapper(new Throwable())"
  }
}

Основной проект

object StackTraceUsage extends App {

  import so.stacktrace.StackTraceWrapper

  case class Error(msg: String, stackTrace: List[String])

  object Error {
    def apply(msg: String)(implicit stw: StackTraceWrapper): Error = new Error(msg, stw.stackTrace)
  }


  def badMethod(): Unit = {
    println(Error("Test error"))
  }

  def badMethodCaller():Unit = {
    badMethod()
  }

  badMethodCaller()
}

Это работает для менякак я и ожидал

0 голосов
/ 28 декабря 2018

Создайте Throwable в вызывающей стороне, где бы вы ни хотели начать трассировку стека,

def badMethod(...): IO[Error, ...] = {
    ...
    val t = new Throwable
    IO.fail(printError("This is bad", t))
}

, а затем передайте его в printError метод:

def printError(errorMessage: String, t: Throwable): Error = {
   new Error(errorMessage, t.getStackTrace.toList.map(_.toString))
}

Я не знаю, чего хочет второй параметр в вашем Error классе, но я полагаю из вашего вопроса, что он хочет List[String].Если бы это был String, вместо этого вы могли бы позвонить t.getStackTraceString.

Сказав, что если бы вы просто использовали Throwable вместо Error класса, все это было бы сделано для вас ...но я предполагаю, что у тебя есть свои причины.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...