1

I have the following code:

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")
    }
  }
}
...

As you can see, I have mesureTime and handleError functions that I pass to them the name of the function as String, is there way to make it implicitly, I mean its will take the function Name, if not - there way to extract the function Name and print it, also regarding params.

Zvi Mints
  • 1,072
  • 1
  • 8
  • 20
  • Does this answer your question? [Is it possible to recover the name of the function from within the function in scala?](https://stackoverflow.com/questions/3105002/is-it-possible-to-recover-the-name-of-the-function-from-within-the-function-in-s) – Tomer Shetah Jan 20 '21 at 08:37
  • Its not cover s it all, is there a way to make `messureTime` and `handleError` get this value implicitly? (Also if there some way of taking the parms) – Zvi Mints Jan 20 '21 at 08:39

2 Answers2

2

Calculate it inside Metrics:

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

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

Then for example:

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

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

will output:

a1
a2

Is this a safe operation?

If we had:

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

we get:

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)

So we see that:

  • In index 0 we get getStackTrace.
  • In index 1 we have currentMethodName.
  • In index 2 we have measureTime.

Since measureTime is not the first method of the stack trace, fir sure we have another element in the stack trace. Therefore in your case yes, it is safe.

Tomer Shetah
  • 8,413
  • 7
  • 27
  • 35
  • I could not find the relevant stack trace since its came from the recover method – Zvi Mints Jan 21 '21 at 08:24
  • @ZviMints It's ok to use the other solution. It is a good solution. The reason you got that behaviour is that you called `currentMethodName` inside the `recover` section. You should have called it before `Metrics.measureTime`. – Tomer Shetah Jan 21 '21 at 08:51
  • Exectually, but then i can just print the name of the function, I want to hand it implicitly on the `recover`, the `messureTime` is less important for me. – Zvi Mints Jan 21 '21 at 08:54
2

You can solve this with a macro (and no runtime reflection):

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]
}

Example:

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")))
  }
}

Yields:

Test.this.foo()
Test.this.bar(1)
Test.this.baz("yes")
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Nice! How can I send CaptureMethod implicitly from the recover? (In my code on the question) – Zvi Mints Jan 21 '21 at 08:23
  • @ZviMints What would you like to do inside recover exactly? – Yuval Itzchakov Jan 21 '21 at 08:57
  • I would like to call `handleError` (with or without additional fields [In that case, handle implicitly]) which can print the caller function (in this example - `disableRules(someId)` or `activeRules(someId)` – Zvi Mints Jan 21 '21 at 09:03
  • @ZviMints I see, well that would be a bit more tricky as we need to pass in the calling function ATM, I'm not sure if a macro can implicitly infer the context it's in. – Yuval Itzchakov Jan 21 '21 at 09:54
  • Is there maybe some other way to get the data on compile time? – Zvi Mints Jan 21 '21 at 11:22