用于记录目的的Scala反射

时间:2015-03-04 11:11:09

标签: scala

在Scala中,是否可以获取包含代码的成员/函数的名称?例如为了完成像

这样的代码
def someFunc() {
  Log(s"error $x occurred in function ${SomeScalaAPI.enclosingFunction}")
}

会产生一个字符串:

"错误X出现在函数someFunc"

不诉诸creating and catching exceptions?我想假设,因为异常知道它们发生在哪里,这些信息可以通过一些Scala或Java反射API获得,或者通过一个聪明的编译时宏?

谢谢!

2 个答案:

答案 0 :(得分:6)

一些宏观魔法的时间:

import language.experimental.macros

import reflect.macros.blackbox.Context

case class Location(filename: String, line: Int, column: Int, enclosingMethod : Option[String])

object PositionMacro {
  def currentLocation: Location = macro impl

  def impl(c: Context): c.Expr[Location] = {
    import c.universe._
    val pos = c.macroApplication.pos
    val owner = c.internal.enclosingOwner //Old, deprecated way: c.enclosingMethod.symbol
    val enclosingMethod = Some(owner).filter(_.isMethod).map(_.fullName)
    c.Expr(q"Location(${pos.source.path}, ${pos.line}, ${pos.column}, ${enclosingMethod})")
  }
}

你可以像这样使用它:

def test(): Unit = {
  println(s"Called from ${PositionMacro.currentLocation}")
}

test()
// Called from Location(C:\Users\sschwets\AppData\Local\Temp\scratchPad.sc9.tmp,9,41,Some(A$A22.A$A22.test))

我将以下内容添加到我的build.sbt文件中:

scalaVersion := "2.11.4"

libraryDependencies +=
  "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided"

注意:这是我第一个使用Scala 2.11接口和准引号的宏。它对我来说很好,但可能会格式化你的硬盘,清空你的银行帐户,宠坏你的孩子,愤怒你的猫或与你的妻子/丈夫/无论如何逃跑。所以请小心!

答案 1 :(得分:2)

您可以使用java:

java.lang.Thread.currentThread.getStackTrace()(1).getMethodName

示例:

scala> def aa = {println(java.lang.Thread.currentThread.getStackTrace()(1).getMethodName)}
aa: Unit

scala> aa
aa

但它不适用于函数 - 仅适用于方法:

scala> val zz = aa _
zz: () => Unit = <function0>

scala> zz()
aa

scala> val zzz = () => println(java.lang.Thread.currentThread.getStackTrace()(1).getMethodName)
zzz: () => Unit = <function0>

scala> zzz()
apply$mcV$sp

如果你正在寻找一些编译时宏,那就有一个类似的解决方案 - https://github.com/dk14/println-tracer;用法示例:

@trace class MyF {
  def call(param: Int): Int = if (param == 0) param else call(param - 1)
  def call2(param2: Int) = param2
  def call3(param2: Int) = ???
}

(new MyF).call(2)
(new MyF).call2(666)
Try{(new MyF).call3(666)}

结果:

 call(param = 2)
  call(param = 1)
   call(param = 0)
   call = 0
  call = 0
 call = 0
 call2(param2 = 666)
 call2 = 666
 call3(param2 = 666)
 call3 = scala.NotImplementedError: an implementation is missing

它使用println而不是logger(可修复)并且需要宏天堂库。所有参数/错误都会自动打印,这可能不是您想要的,但如果没有可用于日志记录的库,则可以将其用作模板。