Reputation: 13706
Is it possible in Scala, to get the name of the member/function containing your code? e.g. in order to accomplish code like
def someFunc() {
Log(s"error $x occurred in function ${SomeScalaAPI.enclosingFunction}")
}
that would produce a string:
"error X occurred in function someFunc"
without resorting to creating and catching exceptions? I would like to assume since exceptions know where they happen, this information might be available through some Scala or Java reflection api.. or, via a clever compile time macro?
Thanks!
Upvotes: 2
Views: 197
Reputation: 8932
Time for some macro magic:
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})")
}
}
You can use it like this:
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))
I added the following to my build.sbt
file:
scalaVersion := "2.11.4"
libraryDependencies +=
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided"
Attention: This is my very first macro with the Scala 2.11 interface and quasi quotes. It works fine for me, but might format your hard disc, empty your bank account, spoil your children, anger your cat or run away with your wife / husband / whatever. So please be careful!
Upvotes: 6
Reputation: 22374
You can do it with java:
java.lang.Thread.currentThread.getStackTrace()(1).getMethodName
Example:
scala> def aa = {println(java.lang.Thread.currentThread.getStackTrace()(1).getMethodName)}
aa: Unit
scala> aa
aa
But it won't work for functions - only for methods:
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
If you're looking for some compile-time macro, there is a simmilar solution - https://github.com/dk14/println-tracer; example of usage:
@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)}
Results:
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
It uses println
instead of logger (which is fixable) and requires macro-paradise library. All parameters/errors are printed automatically, which may be not what you want, but you can use it as a template if there is no logging-ready library around.
Upvotes: 1