隐式提取 Scala 函数名称和参数

Extract Scala function name and params implicitly

我有以下代码:

def disableRules(someId: String) = Action.async { implicit req =>
   Metrics.measureTime("disableRules") {
     someFutureOpr(someId).map(_ => Ok)
     .recover {
       case e: Exception => handlerError(s"Failure occurred on disableRules request ${e.getMessage}", "disableRules")
     }
   }
 }

def activeRules(someId: String) = Action.async { implicit req =>
  Metrics.measureTime("activeRules") {
    someFutureOpr2(someId).map(_ => Ok)
    .recover {
      case e: Exception => handlerError(s"Failure occurred on activeRules request ${e.getMessage}", "activeRules")
    }
  }
}
...

如您所见,我将 mesureTimehandleError 函数传递给它们 name 作为字符串的函数,有没有办法隐含地实现它,我的意思是它将采用函数名称,如果没有 - 有办法提取函数名称并打印它,也关于参数。

里面计算一下Metrics:

object Metrics {
  def currentMethodName() : String = Thread.currentThread.getStackTrace()(3).getMethodName

  def measureTime(): Unit = {
    println(currentMethodName)
  }
}

然后例如:

def a1() = {
  Metrics.measureTime()
}

def a2() = {
  Metrics.measureTime()
}

将输出:

a1
a2

这样操作安全吗?

如果我们有:

def currentMethodName() : String = Thread.currentThread.getStackTrace.toList.mkString("\n")

我们得到:

java.lang.Thread.getStackTrace(Thread.java:1559)
HelloWorld1$Metrics$.currentMethodName(HelloWorld1.scala:69)
HelloWorld1$Metrics$.measureTime(HelloWorld1.scala:72)
HelloWorld1$.a1(HelloWorld1.scala:77)
HelloWorld1$.main(HelloWorld1.scala:103)
HelloWorld1.main(HelloWorld1.scala)

所以我们看到:

  • 在索引 0 中我们得到 getStackTrace
  • 在索引 1 中我们有 currentMethodName
  • 在索引 2 中我们有 measureTime

因为 measureTime 不是堆栈跟踪的第一个方法,所以确定我们在堆栈跟踪中还有另一个元素。因此在你的情况下是的,它是安全的。

你可以用一个宏来解决这个问题(没有运行时反射):

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

class CaptureImpl(val c: blackbox.Context) {
  import c.universe._

  def describe[T: c.WeakTypeTag](
      expr: c.Expr[T]
  ): c.Expr[String] = c.Expr[String](q"(${expr.tree.toString()})")
}

object CaptureMethod {
  def apply[T](expr: T): String = macro CaptureImpl.describe[T]
}

示例:

object Test {
  def foo(): String = "hello"
  def bar(a: Int): Int = a
  def baz(s: String): String = s
  
  def main(args: Array[String]): Unit = {
    println(CaptureMethod(foo()))
    println(CaptureMethod(bar(1)))
    println(CaptureMethod(baz("yes")))
  }
}

产量:

Test.this.foo()
Test.this.bar(1)
Test.this.baz("yes")