Using Scala's macros to build a better log()

28 Sep 2015

I'd like to build a better log function. You know, normally, you have to log variables like this:

log(s"Some msg [count = ${count}]")

You can see that count is redundant. We should be able to do something like:

log("Some msg", count) > "Some msg [count = 10]"

It turns out we can do it with Scala's macros. It can read the variable name, and modify the abstract syntax tree to print out “[count = .. ]”

object MacroLog { def log(msg: String, params: Any*): Unit = macro log_impl def log_impl(c: Context)(msg: c.Expr[String], params: c.Expr[Any]*): c.Expr[Unit] = { import c.universe._ val msgTree = reify { print(msg.splice) }.tree def mkParamTree(param: c.Expr[Any]) = { val expr = c.Expr[String](Literal(Constant(show(param.tree)))) reify { val name = expr.splice.split('.').last print(s"$name = ${param.splice}") }.tree } val paramTrees = params match { case head :: tail => List(reify { print(" [") }.tree) ++ List(mkParamTree(head)) ++ tail.flatMap { pt => Seq(reify { print(", ") }.tree, mkParamTree(pt)) } ++ List(reify { println("]") }.tree) case Nil => Nil } c.Expr[Unit](Block(msgTree :: paramTrees, Literal(Constant(())))) } }

The code is pretty short.. I am too lazy to explain it… so…

Give it a kudos