[英]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.