优化的日志方式

package macros_demo

import scala.language.experimental.macros
import org.slf4j._
import scala.reflect.macros.whitebox.Context object Macros {  
 implicit class LoggerEx(val logger: Logger) {    
   def DEBUG(msg: String): Unit = macro LogMacros.DEBUG1
   def DEBUG(msg: String, exception: Exception): Unit = macro LogMacros.DEBUG2  }  
 object LogMacros {    
   def DEBUG1(c: Context)(msg: c.Tree): c.Tree = {      
     import c.universe._      
     val pre = c.prefix
     q"""
        val x = $pre.logger
        if( x.isDebugEnabled ) x.debug($msg)
      """
   }    
   def DEBUG2(c:Context)(msg: c.Tree, exception: c.Tree): c.Tree = {      
     import c.universe._      
     val pre = c.prefix
     q"""
        val x = $pre.logger
        if(x.isDebugEnabled) x.debug( $msg, $exception )
      """
   }
 }
} package macros_test import org.slf4j._
import macros_demo.Macros._ class LogTest {  
 val logger = LoggerFactory.getLogger(getClass)
 logger.DEBUG(s"Hello, today is ${new java.util.Date}")
}

在这个例子中:

  • 我们通过隐式转换的方式,为 org.slf4j.Logger 扩展了 DEBUG 方法,使用上与 原有的debug 一致,我们期望新的 DEBUG 匹配如下的模式:

// logger.DEBUG(message) will expand to at compile timeif(logger.isDebugEnabled) logger.debug(message)
  • 可以使用这个选项来看看 scala 编译生成的代码:(可以直接在sbt中 set scalacOption := Seq(“-Ymacro-debug-lite”)开启选项)

  val x = macros_demo.Macros.LoggerEx(LogTest.this.logger).logger;  
 if (x.isDebugEnabled)
   x.debug(scala.StringContext.apply("Hello, today is ", "").s(new java.util.Date()))  
 else
   ()
 
//Block(List(ValDef(Modifiers(), TermName("x"), TypeTree(), Select(Apply(Select(Select(Ident(macros_demo), macros_demo.Macros), TermName("LoggerEx")), List(Select(This(TypeName("LogTest")), TermName("logger")))), TermName("logger")))), If(Select(Ident(TermName("x")), TermName("isDebugEnabled")), Apply(Select(Ident(TermName("x")), TermName("debug")), List(Apply(Select(Apply(Select(Select(Ident(scala), scala.StringContext), TermName("apply")), List(Literal(Constant("Hello, today is ")), Literal(Constant("")))), TermName("s")), List(Apply(Select(New(Select(Select(Ident(java), java.util), java.util.Date)), termNames.CONSTRUCTOR), List()))))), Literal(Constant(()))))

上面的第一段代码,是 scalac 生成的等效代码,可以看到,已经符合了我们的预期,尽在debug级别生效时,才会对messgae进行求助计算,避免不必要的开销,使得这段代码,在debug级别关闭时,基本上没有任何性能的损失。

而第二段代码,有如天书,难以阅读。其实,这就是scalac内部的对这一段代码的表示格式,一般的,我们称之为 Abstracted Syntax Tree(AST),有兴趣的同学,可以通过这个网站 *AST explorer* 来帮助阅读AST。 scala 2.10时代,写macro,就必须自己来构建AST,相当于你要徒手写出这么复杂的一个表达式,这是一件近乎不可完成的任务,所以,macro书写的难度时及其至高的,好在后续的版本中提供了 q”” 插值,我们可以直接使用q”val x = $pre.logger; if( x.isDebugEnabled ) x.debug($msg)”来替代上面这么一个复杂的AST,让 macro 的编写门槛极大幅度的降低下来。

不过,即使这样,要想很好的驾驭macro,你还是要懂一些 AST 的知识,否则,还是很难的。 所以,书写Macro,其实就是一个和编译器协同工作的过程,这就是macro的难度之所在。或许,未来,随着 scalameta 和 dotty的成熟,macro的编写可以进一步的降低吧。

参考:

神奇的Scala Macro之旅(一)- 什么时候用宏

转自:神奇的Scala Macro之旅(2)

神奇的Scala Macro之旅(二)- 一个实例的更多相关文章

  1. 神奇的Scala Macro之旅(三)- 实际应用

    在上一篇中,我们示范了使用macro来重写 Log 的 debug/info 方法,并大致的介绍了 macro 的基本语法.基本使用方法.以及macro背后的一些概念, 如AST等.那么,本篇中,我们 ...

  2. 神奇的Scala Macro之旅(一)- 什么时候用宏

    在Lisp语言中,macro是一个神器,可以“动态的生成代码”,然后被执行,这种方式给到Lisp无限的表达能力.除Lisp之外,很少有语言支持Macro这个特性,我记得 GWT之中曾经有一个类似的Ge ...

  3. 神奇的Scala Macro之旅(四)- BeanBuilder

    在Java开发中,经常会有一个需求,将一个 Bean 复制到另外一个 Bean,尤其是在后台分层的场景下,在不同的层之间传递信息,经常需要进行 这样的一个对象复制工作,类似于: val source: ...

  4. WCF学习之旅——第一个WCF示例(二)

    第四步:通过自我寄宿的方式寄宿服务 WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程.WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段. 终 ...

  5. WCF学习之旅——第一个WCF示例(一)

    最近需要用到WCF,所以对WCF进行了解.在实践中学习新知识是最快的,接下来先做了一个简单的WCF服用应用示例. 本文的WCF服务应用功能很简单,却涵盖了一个完整WCF应用的基本结构.希望本文能对那些 ...

  6. WCF学习之旅——第一个WCF示例(三)

    第五步:创建客户端 WCF应用服务被成功寄宿后,WCF服务应用便开始了服务调用请求的监听工作.此外,服务寄宿将服务描述通过元数据的形式发布出来,相应的客户端就可以获取这些元数据.接下来我们来创建客户端 ...

  7. scala函数式编程(二) scala基础语法介绍

    上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些ja ...

  8. Scala学习笔记(二)表达式和函数

    笔记的整理主要针对Scala对比Java的新特性:   1.if表达式 if表达式是有结果返回的. val a= if (5>2) "你好" else 1 a的值为if表达式 ...

  9. Akka.NET是Java/Scala 流行框架Akka的一个 .NET 开源移植

    Akka.NET v1.0 已发布,支持Mono Akka.NET 是Java/Scala 流行框架Akka的一个 .NET 开源移植.可用于构建高并发,分布式和容错事件驱动的应用在 .NET 和 M ...

随机推荐

  1. IT桔子沙龙之本地生活服务O2O探路者笔记整理

    这个沙龙活动侧重于创业融资与投资角度 主持人:真格基金投资分析师顾女士;E-mail:grace@zhenfund.com;新浪微博:@顾三小姐 赞助商七牛云的服务宣传:静态资源托管:上传下载全网加速 ...

  2. 关于php中openssl_public_encrypt无填充的一个注意事项

    昨天在使用openssl_public_encrypt函数rsa加密一些数据传输的时候,怎么都是加密失败. if (openssl_public_encrypt($data, $encrypted, ...

  3. 3D Lut 电影级调色算法 附完整C代码

    在前面的文章,我提到过VSCO Cam 的胶片滤镜算法实现是3d lut. 那么3d lut  到底是个什么东西呢? 或者说它是用来做什么的? 长话短说,3d lut(全称 : 3D Lookup t ...

  4. 卸载重装Mysql

    卸载重装前请备份数据库 卸载 sudo apt autoremove --purge mysql-server-core-5.7 清理残留 sudo rm -r /var/lib/mysql* sud ...

  5. Java中的String类型

    1.基本类型和引用类型 在C语言里面,是有指针这么一个变量类型的,指针变量保存的就是所要指向内容的地址.在Java里面,没有了指针的这么个说法,而是换了一个词:引用类型变量. 先说Java里面的基本类 ...

  6. Java 学习路线之四个阶段

    写这篇总结,主要是记录下自己的学习经历,算是自己对知识的一个回顾.也给想要学习 Java 的提供一些参考,对于一些想要学习Java,又不知道从哪里下手,以及现在有哪些主流的 Java 技术.想必大家学 ...

  7. JavaScript单线程和异步机制

    随着对JavaScript学习的深入和实践经验的积累,一些原理和底层的东西也开始逐渐了解.早先也看过一些关于js单线程和事件循环的文章,不过当时看的似懂非懂,只留了一个大概的印象:浏览器中的js程序时 ...

  8. 玩转PHP(二)--PHP强大的时间函数:date()

    PHP具有相对来说强大的时间函数date(),该方法有下列一系列参数: 例如: echo date("Y-m-d H:i:s"); //2015-01-09 13:03:30 如果 ...

  9. 大话RabbitMQ 基础入门

    ----------写在前面---------- 近些年微服务越来越火,让我也忍不住想去一窥微服务究竟,讲到微服务,就离不开分布式,而分布式,也离不开消息队列,在消息队列中,RabbitMQ可以说是比 ...

  10. Python集合set

    集合 set 集合是无序的 集合的值是唯一的 求两个集合的关系: list1 = [1,4,5,7,3,6,7,9] list2 = set([2,6,0,66,22,8,4]) list3 = se ...