Java日志API开销



我已经阅读了一些关于用Java记录调试消息的各种方法,并且来自C背景,我的关注如下:

这些库声称在禁用日志记录(例如生产环境)的情况下开销最小,但由于对它们的log()函数的参数仍然是评估的,我担心的是,在现实场景中的开销实际上根本不能忽略。

例如,即使log函数本身不做任何事情,log(myobject.toString(), "info message")仍然有计算myobject.toString()的开销,这可能相当大。

谁有解决这个问题的办法?

PS:对于那些想知道为什么我提到C背景的人:C允许您使用预处理器宏和编译时指令,这些指令将完全删除与编译时调试相关的所有代码,包括宏参数(根本不会出现)。

编辑:在阅读了第一批答案后,似乎java显然没有任何东西可以做到这一点(想想在移动环境中记录大循环中一个数字的余弦,其中每一点CPU都很重要)。所以我补充说,我甚至会选择基于IDE的解决方案。我最后的手段是构建类似于"查找所有/替换"宏的东西。我最初的想法是,也许从面向方面的框架中抓取一些东西会有所帮助……有人知道吗?

我认为log4j FAQ很好地解决了这个问题:

对于某些记录器,写入

l.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

引起构造消息参数的开销,即将整数i和条目[i]转换为String,并连接中间字符串。无论消息是否将被记录,

如果你担心速度,那么写

 if(l.isDebugEnabled()) {
     l.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
 }

这样,如果记录器1的调试被禁用,您将不会产生参数构造的成本。另一方面,如果记录器已启用调试,您将产生评估记录器是否启用的成本,两次:一次在debugEnabled中,一次在debug中。这是一个微不足道的开销,因为评估一个日志记录器所花费的时间少于实际记录一条语句所花费时间的1%。

使用保护子句是这里避免字符串构造的一般方法。

其他流行的框架,如slf4j,采用使用格式化字符串/参数化消息的方法,以便除非需要,否则不会对消息进行计算。

现代日志框架有变量替换。您的日志记录看起来像这样:

log.debug("The value of my first object is %s and of my second object is %s", firstObject, secondObject).

给定对象的toString()只会在日志设置为debug时执行。否则,它将忽略参数并返回

答案很简单:不要在日志调用本身中调用开销大的方法。另外,如果无法避免,请在日志调用周围使用守卫。

 if(logger.isDebugEnabled()) {
    logger.debug("this is an "+expensive()+" log call.");
 }

并且正如其他人指出的那样,如果您的日志框架中有可用的格式(即,如果您使用的是一个足够现代的支持它的框架,它应该是每一个,但不是),您应该依靠它来帮助支付日志记录点上的费用。如果您选择的框架不支持格式化,那么切换或编写您自己的包装器

您是对的,计算log()调用的参数可以增加不必要的开销,并且可能非常昂贵。

这就是为什么大多数正常的日志框架也提供了一些字符串格式化功能,这样你就可以写这样的东西:

log.debug("Frobnicating {0}", objectWithExpensiveToString);
这样你的开销只有是对debug()的调用。如果该级别被取消激活,则不再执行任何操作,如果它被激活,则解释格式字符串,调用objectWithExpensiveToString()上的toString(),并在记录结果之前将结果插入格式字符串。

一些日志语句使用MessageFormat风格的占位符({0}),另一些使用format()风格的占位符(%s),还有一些可能采用第三种方法。

您可以使用一种有趣的方式(虽然有点冗长)来处理断言。当断言打开时,将有输出和开销,当断言关闭时,没有输出和绝对没有开销。

public static void main(String[] args) {
    assert returnsTrue(new Runnable() {
        @Override
        public void run() {
            // your logging code
        }
    });
}
public static boolean returnsTrue(Runnable r) {
    r.run();
    return true;
}

这里需要returntrue()函数,因为我知道没有更好的方法使表达式返回true,并且assert需要一个布尔值。

相关内容

  • 没有找到相关文章

最新更新