简体   繁体   English

检测(记录)Scala表达式

[英]Instrumenting (logging) Scala expressions

( UPD after @krivachy.akos ) (@ krivachy.akos之后的UPD

How to debug expressions in Scala? 如何在Scala中调试表达式? You don't have an opportunity to set a breakpoint and see local variables in most cases because inside the expression there are no variables. 大多数情况下,您没有机会设置断点并查看局部变量,因为在表达式内部没有变量。 And usually there are no statements to which you can set a breakpoint. 通常,没有可设置断点的语句。

One old way of debugging is to have the code instrumented. 调试的一种旧方法是对代码进行检测。 This gives an indispensable information about the internal processing of expressions. 这提供了有关表达式内部处理的必不可少的信息。

However, in a typical logging implementation there are no direct ways of intercepting expressions. 但是,在典型的日志记录实现中,没有截取表达式的直接方法。 In particular, a typical logger have methods with Unit return type: 特别是,典型的记录器具有Unit返回类型的方法:

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

To use the logger one have to rewrite concise expression in a way to be able to call logger: 要使用记录器,必须以一种能够调用记录器的方式重写简洁的表达式:

Example 1 : 范例1

if you have some boolean-based rules with complex conditions and multiple evaluation paths: 如果您有一些具有复杂条件和多个评估路径的基于布尔的规则:

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

then it is hard to make sure it works as desired. 那么很难确保它能够按需工作。 ( It's not always possible to avoid complex rules altogether. And representation rules in OOP-style is not always good.) (并非总是可以完全避免使用复杂的规则。而且,OOP风格的表示规则并不总是很好。)

Then a typical instrumentation would require introduction of some intermediate variables and blocks of code: 然后,典型的检测将需要引入一些中间变量和代码块:

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
        }

Example 2 : 范例2

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

Instrumentation will lead to some intermediate variables: 检测将导致一些中间变量:

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
}

Very ugly, I suppose. 我想很丑。 And this instrumentation takes more space than the code itself. 而且这种检测比代码本身占用更多的空间。 The main reason, I think, is that the logger is incompatible with an expression evaluation style. 我认为,主要原因是记录器与表达式评估风格不兼容。

Is there a way to log expression values in a more concise way? 有没有一种方法可以更简洁地记录表达式值?

I recently found a nice way of logging the value of an expression: 我最近发现了一种记录表达式值的好方法:

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

}

Here how it is used: 这里是如何使用的:

Example 1 (rules): 示例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

Example 2: 范例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="+_)

It can also be used everywhere in expressions: 它也可以在表达式中的任何地方使用:

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

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

Of course you should mix-in Logging trait to your class. 当然,您应该在班级中混入Logging特性。

This kind of instrumentation doesn't hide the logic of the code. 这种检测不会隐藏代码的逻辑。

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

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