繁体   English   中英

检测(记录)Scala表达式

[英]Instrumenting (logging) Scala expressions

(@ krivachy.akos之后的UPD

如何在Scala中调试表达式? 大多数情况下,您没有机会设置断点并查看局部变量,因为在表达式内部没有变量。 通常,没有可设置断点的语句。

调试的一种旧方法是对代码进行检测。 这提供了有关表达式内部处理的必不可少的信息。

但是,在典型的日志记录实现中,没有截取表达式的直接方法。 特别是,典型的记录器具有Unit返回类型的方法:

def debug(msg: =>String) {...}

要使用记录器,必须以一种能够调用记录器的方式重写简洁的表达式:

范例1

如果您有一些具有复杂条件和多个评估路径的基于布尔的规则:

val x = if(condition1(a,b)) 
          Some(production1(a,b))
        else if(condition2(c,d))
          Some(production2(a,b))
        else
          None

那么很难确保它能够按需工作。 (并非总是可以完全避免使用复杂的规则。而且,OOP风格的表示规则并不总是很好。)

然后,典型的检测将需要引入一些中间变量和代码块:

debug("a="+a)
debug("b="+b)
val x = if(condition1(a,b)) {
          debug("rule1 hit")
          val production = production1(a,b)
          debug("rule1 result: "+production)
          Some(production)
        } else { 
          debug("rule1 doesn't hit")
          debug("c="+c)
          debug("d="+d)
          if(condition2(c,d)){
            debug("rule2 hit")
            Some(production2(a,b))
          } else
            None
        }

范例2

def f(list:List[Int]) = 
    list.
        map(_*2).
        flatMap(t =>
            (0 until t).
            map(_+1)
        )

检测将导致一些中间变量:

def f(list:List[Int]) = {        
    val twiced = list.map(_*2)
    debug(s"twiced = $twiced")
    val result = twiced.flatMap(t => {
        val widened = (0 until t).map(_+1)            
        debug(s"widened = $widened")
        widened
    })
    debug(s"result = $result")
    result
}

我想很丑。 而且这种检测比代码本身占用更多的空间。 我认为,主要原因是记录器与表达式评估风格不兼容。

有没有一种方法可以更简洁地记录表达式值?

我最近发现了一种记录表达式值的好方法:

trait Logging {

    protected val loggerName = getClass.getName
    protected lazy val logger = LoggerFactory.getLogger(loggerName)

    implicit class RichExpressionForLogging[T](expr: T){
        def infoE (msg: T ⇒ String):T = {if (logger.isInfoEnabled ) logger.info (msg(expr)); expr}
        def traceE(msg: T ⇒ String):T = {if (logger.isTraceEnabled) logger.trace(msg(expr)); expr}
        def debugE(msg: T ⇒ String):T = {if (logger.isDebugEnabled) logger.debug(msg(expr)); expr}      
        def infoL (msg: String):T = {if (logger.isInfoEnabled ) logger.info (msg+expr); expr}
        def traceL(msg: String):T = {if (logger.isTraceEnabled) logger.trace(msg+expr); expr}
        def debugL(msg: String):T = {if (logger.isDebugEnabled) logger.debug(msg+expr); expr}       
    }

}

这里是如何使用的:

示例1(规则):

val x = if(condition1(a.debugL("a="),b.debugL("b="))) 
      Some(production1(a,b).debugL("rule1="))
    else if(condition2(c,d))
      Some(production2(a,b).debugL("rule2="))
    else
      None

范例2:

def f(list:List[Int]) = 
    list.
        map(_*2).
        debugE(s"res1="+_).
        flatMap(t => (0 until t).
            map(_+1).
            debugE(s"res2="+_)).
        debugE(s"res="+_)

它也可以在表达式中的任何地方使用:

if((a<0).debugE(s"a=$a<0="+_))

for{
    a <- f(list).debugE("f(list)="+_)
    b <- a.debugL("a=")
} yield b.debugL("b=")

当然,您应该在班级中混入Logging特性。

这种检测不会隐藏代码的逻辑。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM