Я не думаю, что есть действительно хорошее решение этой проблемы.Я думаю, что вы должны выбрать один из следующих вариантов, каждый из которых имеет свои недостатки:
Live с заголовком стека printError
.Я не уверен, действительно ли это плохой вариант.Между прочим, существует простой вызов Thread.getStackTrace()
, который можно использовать, как в Thread.currentThread.getStackTrace()
Добавить явный аргумент printError
Попытайтесь взломать возвращенный StackTraceElement[]
, чтобы удалить некоторые верхние элементы, которые вы не хотите видеть.Помните, что это может быть не так просто, потому что потенциально компилятор может встроить некоторые вызовы методов.
Создайте собственное решение на основе макросов, например, ScalaTest Position .Я не думаю, что вы можете сделать это без использования макроса, хотя.С другой стороны, макрос не должен быть сложным, потому что у макросов Context
есть свойство enclosingPosition
.Но имейте в виду, что макросы являются одной из наименее стабильных частей Scala, поэтому они могут быть не очень перспективными.
Непосредственно используйте 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()
}
Это работает для менякак я и ожидал