我在我的应用程序中使用Log4J进行日志记录。之前我使用的调试调用如下:

选项1:

logger.debug("some debug text");

但是一些链接建议最好先检查isDebugEnabled(),比如:

选项2:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text");
}

所以我的问题是“选项2是否能提高性能?”

因为在任何情况下Log4J框架对debugEnabled都有相同的检查。对于选项2,如果我们在一个方法或类中使用多个调试语句可能是有益的,这样框架就不需要多次调用isDebugEnabled()方法(每次调用);在这种情况下,它只调用isDebugEnabled()方法一次,如果Log4J被配置为调试级别,那么实际上它会调用isDebugEnabled()方法两次:

如果将值分配给debugEnabled变量,和 实际上由logger.debug()方法调用。

我不认为如果我们在方法或类中编写多个logger.debug()语句并根据选项1调用debug()方法,那么与选项2相比,Log4J框架的开销是很大的。由于isDebugEnabled()是一个非常小的方法(就代码而言),因此它可能是内联的良好候选方法。


当前回答

我建议大多数人使用选项2,因为它不是特别贵。

案例1: log.debug(一个字符串)

例2: Log.debug(“一个字符串”+“两个字符串”+对象。toString + object2.toString)

在调用这两个函数时,必须计算log.debug中的参数字符串(无论是CASE 1还是Case2)。这就是人们所说的“昂贵”。如果你在它之前有一个条件,'isDebugEnabled()',这些不需要计算,这是性能保存的地方。

其他回答

比如@erickson,这要看情况。如果我没记错的话,isDebugEnabled已经在Log4j的debug()方法中构建了。 在我看来,只要你不在调试语句中做一些昂贵的计算,比如在对象上循环,执行计算和连接字符串,你就可以了。

StringBuilder buffer = new StringBuilder();
for(Object o : myHugeCollection){
  buffer.append(o.getName()).append(":");
  buffer.append(o.getResultFromExpensiveComputation()).append(",");
}
log.debug(buffer.toString());

会更好

if (log.isDebugEnabled(){
  StringBuilder buffer = new StringBuilder();
  for(Object o : myHugeCollection){
    buffer.append(o.getName()).append(":");
    buffer.append(o.getResultFromExpensiveComputation()).append(",");
  }
  log.debug(buffer.toString());
}

正如其他人提到的,只有在创建字符串是一个耗时的调用时,使用guard语句才真正有用。具体的例子是当创建字符串时会触发一些延迟加载。

值得注意的是,这个问题可以通过使用Java的简单日志Facade或(SLF4J) - http://www.slf4j.org/manual.html来完成避免。这允许调用如下方法:

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);

这只会在启用调试时将传入的参数转换为字符串。SLF4J顾名思义只是一个facade,日志调用可以传递给log4j。

你也可以很容易地“滚动”你自己的版本。

希望这能有所帮助。

使用isDebugEnabled()是为当你通过连接字符串来构建日志消息保留的:

Var myVar = new MyVar();
log.debug("My var is " + myVar + ", value:" + myVar.someCall());

然而,在您的示例中,没有速度增益,因为您只是记录一个字符串,而不执行连接等操作。因此,你只是在给你的代码增加膨胀,让它更难阅读。

我个人在String类中使用Java 1.5格式调用,如下所示:

Var myVar = new MyVar();
log.debug(String.format("My var is '%s', value: '%s'", myVar, myVar.someCall()));

我怀疑是否有很多优化,但它更容易阅读。

不过请注意,大多数日志api都提供了这样开箱即用的格式:例如,slf4j提供了以下格式:

logger.debug("My var is {}", myVar);

这样就更容易阅读了。

If you use option 2 you are doing a Boolean check which is fast. In option one you are doing a method call (pushing stuff on the stack) and then doing a Boolean check which is still fast. The problem I see is consistency. If some of your debug and info statements are wrapped and some are not it is not a consistent code style. Plus someone later on could change the debug statement to include concatenate strings, which is still pretty fast. I found that when we wrapped out debug and info statement in a large application and profiled it we saved a couple of percentage points in performance. Not much, but enough to make it worth the work. I now have a couple of macros setup in IntelliJ to automatically generate wrapped debug and info statements for me.

选择2更好。

它本身并不能提高性能。但它确保性能不会下降。这是如何。

通常我们认为 logger.debug (someString);

但通常,随着应用程序的发展,会有很多人转手,尤其是新手开发人员

logger.debug(str1 + str2 + str3 + str4);

诸如此类。

即使日志级别设置为ERROR或FATAL,字符串的连接也会发生! 如果应用程序包含大量带有字符串连接的DEBUG级别消息,那么它肯定会受到性能影响,特别是使用jdk 1.4或以下版本时。(我不确定以后版本的jdk内部是否做任何stringbuffer.append())。

这就是为什么选择2是安全的。即使字符串连接也不会发生。