我已经阅读了一些关于使用Java记录调试消息的各种方法,并且来自C背景我的关注点如下:
在禁用日志记录(例如生产环境)的情况下,这些库声称开销最小,但由于仍然评估其log()
函数的参数,我担心的是真实场景中的开销将在事实上,根本不可忽视。
例如,log(myobject.toString(), "info message")
仍然有评估myobject.toString()
的开销,这可能非常大,即使日志函数本身什么都不做。
有没有人能解决这个问题?
PS:对于那些想知道为什么我提到C背景的人:C允许你使用预处理器宏和编译时指令,这些指令将在编译时完全删除与调试相关的所有代码,包括宏参数(不会出现在所有)。编辑: 在阅读了第一批答案之后,似乎java显然没有任何可以解决问题的方法(想想在每个CPU都很重要的移动环境中在一个大循环中记录数字的余弦)。所以我要补充一点,我甚至会选择基于IDE的解决方案。我的最后一招是构建类似“查找所有/替换”宏的东西。 我首先想到的是,从面向方面的框架中抓取的东西可能有助于...... 任何人 ?
答案 0 :(得分:9)
我认为the log4j FAQ可以很好地解决这个问题:
对于某些记录器l,写作,
l.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
会产生构造message参数的成本,即将integer i和entry [i]转换为String,并连接中间字符串。无论是否记录消息,都可以这样做。
如果您担心速度,请写下
if(l.isDebugEnabled()) { l.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); }
这样,如果对logger l禁用调试,则不会产生参数构造的成本。另一方面,如果记录器启用了调试,则会产生评估记录器是否启用的成本,两次:一次在debugEnabled中,一次在调试中。这是一个微不足道的开销,因为评估记录器所花费的时间不到实际记录语句所需时间的1%。
使用保护子句是避免字符串构造的一般方法。
其他流行的框架,例如slf4j,采用using formatted strings / parameterized messages的方法,以便除非需要,否则不会对消息进行评估。
答案 1 :(得分:4)
现代日志记录框架具有可变替换。您的日志记录看起来像这样:
log.debug("The value of my first object is %s and of my second object is %s", firstObject, secondObject).
只有在将logging设置为debug时才会执行给定对象的toString()。否则它将忽略参数并返回。
答案 2 :(得分:3)
答案很简单:不要在日志调用本身中调用昂贵的方法。另外,如果您无法避免,请使用记录呼叫周围的警卫。
if(logger.isDebugEnabled()) {
logger.debug("this is an "+expensive()+" log call.");
}
正如其他人所指出的那样,如果您的日志记录框架中有可用的格式(例如,如果您使用的是现代足以支持它的格式,那么它应该每个但是不是是的,你应该依靠它来帮助在记录点上支付费用。如果您选择的框架不支持格式化,那么请切换或编写自己的包装器。
答案 3 :(得分:2)
你是对的,评估log()
电话的参数可以增加不必要的开销,可能费用昂贵。
这就是为什么大多数理智的日志框架都提供了一些字符串格式化功能,所以你可以编写这样的东西:
log.debug("Frobnicating {0}", objectWithExpensiveToString);
这样,您的唯一开销就是对debug()
的调用。如果该级别被取消激活,则不再执行任何操作,如果已激活,则解释格式字符串,调用toString()
上的objectWithExpensiveToString()
并在记录之前将结果插入格式字符串中
某些日志语句使用MessageFormat
样式占位符({0}
),其他日志语句使用format()
style占位符(%s
),而其他日志语句可能采用第三种方法。
答案 4 :(得分:-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;
}
这里需要returnsTrue()函数,因为我知道没有更好的方法使表达式返回true,而assert需要一个布尔值。