3

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

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

例如, alog(myobject.toString(), "info message")仍然有评估的开销myobject.toString(),这可能非常大,即使 log 函数本身什么也不做。

有没有人有这个问题的解决方案?

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

编辑:在阅读了第一批答案之后,似乎 java 显然没有任何可以解决问题的方法(想想在 CPU 的每一点都很重要的移动环境中将数字的余弦记录在一个大循环中)。所以我要补充一点,我什至会选择基于 IDE 的解决方案。我最后的手段是构建类似“查找全部/替换”宏的东西。我首先想到也许从面向方面的框架中获取的东西会有所帮助......任何人?

4

5 回答 5

9

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

对于一些记录器 l,写作,

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

产生构造消息参数的成本,即将整数 i 和 entry[i] 转换为字符串,并连接中间字符串。这与是否记录消息无关。

如果您担心速度,请写

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

这样,如果对 logger l 禁用调试,您将不会产生参数构造的成本。另一方面,如果记录器启用了调试,您将承担评估记录器是否启用的成本,两次:一次在 debugEnabled 中,一次在调试中。这是一个微不足道的开销,因为评估记录器所花费的时间不到实际记录语句所需时间的 1%。

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

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

于 2011-04-26T16:46:22.717 回答
4

现代日志框架有变量替换。您的日志记录如下所示:

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

给定对象的 toString() 将仅在日志记录设置为调试时执行。否则它将忽略参数并返回。

于 2011-04-26T16:44:47.870 回答
3

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

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

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

于 2011-04-26T16:45:16.833 回答
2

你是对的,评估log()调用的参数增加不必要的开销,而且可能很昂贵。

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

log.debug("Frobnicating {0}", objectWithExpensiveToString);

这样,您唯一的开销就是调用debug(). 如果该级别已停用,则不再执行任何操作,如果已激活,则解释格式字符串,调用toString()onobjectWithExpensiveToString()并将结果插入到格式字符串中,然后再记录它。

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

于 2011-04-26T16:44:52.880 回答
-1

您可以使用一种有趣的方式 - 虽然有点冗长 - 带有断言。打开断言,会有输出和开销,关闭断言,没有输出,绝对没有开销。

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 需要一个布尔值。

于 2011-04-26T17:03:13.183 回答