What is the best way to get the name of the caller class in an object?

I could get this working using this:

scala> object LOGGER {
     | def warning(msg: String)(implicit className:String) = {
     |   className
     | }
     | }
defined object LOGGER

scala> class testing {
     | lazy implicit val className = this.getClass.getName
     | def test = LOGGER.warning("Testing")
     | }
defined class testing

scala> val obj = new testing()
obj: testing = testing@11fb4f69

scala> obj.test
res51: String = testing <=======

scala> class testing2 {
     | lazy implicit val className = this.getClass.getName
     | def test = LOGGER.warning("Testing")
     | }
defined class testing2

scala> val obj2 = new testing2()
obj2: testing2 = testing2@2ca3a203


scala> obj2.test
res53: String = testing2 <=====

I also tried using Thread.currentThread.getStackTrace in the object LOGGER but couldn't get it to print the calling class testing in the warning function. Any other ways to do this?

Answers


Dynamic variable

One way to do it is DymamicVariable

import scala.util.DynamicVariable
object LOGGER {
  val caller = new DynamicVariable[String]("---")
  def time = new Date().toString
  def warning(msg: String) = println(s"[${caller.value} : $time] $msg")
}

trait Logging {
  def logged[T](action: => T) = LOGGER.caller.withValue(this.getClass.getName)(action)
}

class testing extends Logging {
  def test = logged {
    //some actions
    LOGGER.warning("test something")
    //some other actions
  }
}

val t = new testing

t.test

will print something like

[testing : Wed Nov 25 11:29:23 MSK 2015] test something

Or instead of mixing in Logging you can use it directly

class testing {
  def test = LOGGER.caller.withValue(this.getClass.getName) {
    //some actions
    LOGGER.warning("test something")
    //some other actions
  }
}
Macro

Another more powerfull, but more complex to support approach is to build some simple macro

You could define in other source, preferrably in other subproject

import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
class LoggerImpl(val c: Context) {
  import c.universe._

  def getClassSymbol(s: Symbol): Symbol = if (s.isClass) s else getClassSymbol(s.owner)

  def logImpl(msg: Expr[String]): Expr[Unit] = {
    val cl = getClassSymbol(c.internal.enclosingOwner).toString
    val time = c.Expr[String](q"new java.util.Date().toString")
    val logline = c.Expr[String](q""" "[" + $cl + " : " + $time + "]" + $msg """)
    c.Expr[Unit](q"println($logline)")
  }
}

object Logger {
  def warning(msg: String): Unit = macro LoggerImpl.logImpl
}

Now you don't need to change the testing class:

class testing {
  def test = {
    //some actions
    Logger.warning("something happen")
    //some other actions
  }
}

And see desired output.

Thsi could be very-perfomant alternative to runtime stack introspection


I use this technique in my custom classloader project to get the name of the first class up the stack not in my package. The general idea is copied from the UrlClassloader.

String callerClassName="";
StackTraceElement[] stackTrace=Thread.currentThread().getStackTrace();
for (int i=1; i < stackTrace.length; i++) {
  String candidateClassName=stackTrace[i].getClassName();
  if(!candidateClassName.startsWith("to.be.ignored") && 
     !candidateClassName.startsWith("java")){
    callerClassName=candidateClassName;
    break;
  }
}

The approach has it's drawbacks since it only gets the name of the class, not the actual class or even better the object.


Need Your Help

Compile a PHP script in Linux

php linux

I know PHP scripts don't actually compile until they are run. However, say I want to create a small simple program and compile it to a binary without requiring the PHP binary. How could I do this?

"Can not find source" in Eclipse CDT in UBUNTU 14.10

c eclipse ubuntu

My problem is that I try to debug this simple Hello World Program in Eclipse LUNA CDT and UBUNTU 14.10 but get the error- can't find source at /build/buildd/glibc-2.19/stdio-common/printf.c. My Cod...