我想将所有返回自定义类型T
的scala项目中的所有用户定义函数包装到接受T
和函数名称作为参数的函数中。
例如
鉴于此功能在范围之内:
def withMetrics[T](functionName: String)(f: => Try[T]): Try[T] = {
f match {
case _: Success[T] => println(s"send metric: success for $functionName")
case _: Failure[T] => println(s"send metric: failure for $functionName")
}
f
}
用户可以发送其功能的指标,并通过这样做返回Try
def userDefinedFunction: Try[_] =
withMetrics("userDefinedFunction"){
somethingRisky: Try[_]
}
但是我希望用户只需要定义
def userDefinedFunction: Try[_] =
somethingRisky: Try[_]
并且将返回Try
的业务逻辑隐式包装到withMetrics
中。
请注意,用户不必注释代码,因为这可能会使他忘记代码。
相反,应将其项目中定义的所有用户功能自动包装到withMetrics
中。
如何通过使用Scala 2或dotty宏来实现此目标? 还是可以通过其他方式实现?
答案 0 :(得分:2)
您可以创建宏注释,并在其中注释所有要检测方法的类,对象和特征。
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise (or -Ymacro-annotations in 2.13) to expand macro annotations")
class withMetrics extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro WithMetricsMacro.impl
}
object WithMetricsMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def modify(stats: Seq[Tree]): Seq[Tree] = stats.map {
case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" =>
q"$mods def $tname[..$tparams](...$paramss): $tpt = withMetrics(${tname.toString}){ $expr }"
}
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..${modify(stats)} }
..$tail
"""
case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
q"""
$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..${modify(stats)} }
..$tail
"""
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: Nil =>
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..${modify(stats)} }"
case _ =>
c.abort(c.enclosingPosition, "Not a class, object or trait ")
}
}
}
}
import Macros._
import scala.util.{Failure, Success, Try}
object App {
@withMetrics
class A {
def userDefinedFunction: Try[String] = Try("aaa")
}
def withMetrics[T](functionName: String)(f: => Try[T]): Try[T] = {
f match {
case _: Success[T] => println(s"send metric: success for $functionName")
case _: Failure[T] => println(s"send metric: failure for $functionName")
}
f
}
def main(args: Array[String]): Unit = {
(new A).userDefinedFunction // send metric: success for userDefinedFunction
}
}
这不会修改嵌套方法以及内部类,对象,特征中的方法。如有必要,也可以使用scala.reflect.api.Trees.Traverser/Transformer
完成。或者,您可以仅在必要时注释内部类,对象,特征。