在log4j中,在记录改进性能之前检查isDebugEnabled吗?

Sil*_*ior 192 java logging log4j

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

选项1:

logger.debug("some debug text");
Run Code Online (Sandbox Code Playgroud)

但有些链接表明最好先检查一下isDebugEnabled(),例如:

选项2:

boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
    logger.debug("some debug text");
}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是" 选项2能否以任何方式提高性能? ".

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

  1. 如果为debugEnabled变量赋值,和
  2. 实际上是由logger.debug()方法调用的.

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

eri*_*son 230

在这种特殊情况下,选项1更好.

isDebugEnabled()当涉及调用toString()各种对象的方法并连接结果时,保护语句(检查)用于防止可能昂贵的日志消息计算.

在给定的示例中,日志消息是一个常量字符串,因此让记录器丢弃它与检查记录器是否已启用一样高效,并且由于分支较少,因此降低了代码的复杂性.

更好的是使用更新的日志记录框架,其中日志语句采用格式规范和由记录器替换的参数列表 - 但"懒惰",仅当记录器已启用时.这是slf4j采用的方法.

有关更多信息,请参阅我对相关问题的回答,以及使用log4j执行此类操作的示例.

  • log5j扩展log4j的方式与slf4j非常相似 (3认同)
  • @SnakeDoc否.它是方法调用的基础:方法arg-lists中的表达式在调用之前被有效地计算.如果这些表达式是a)被认为是昂贵的并且b)仅在某些条件下需要(例如,当启用调试时),那么您唯一的选择是对调用进行条件检查,并且框架不能为您执行此操作.基于格式化程序的日志方法的一点是你可以传递一些Object(基本上是免费的),只有在需要时,logger才会调用`toString()`. (2认同)

Cek*_*eki 31

由于在方案1的消息字符串是一个常数,也绝对与条件包裹logging语句,相反没有收获,如果日志语句启用调试,你将被评估两次,一次是在isDebugEnabled()方法和一次debug()方法.调用的成本isDebugEnabled()大约为5到30纳秒,对于大多数实际目的而言应该可以忽略不计.因此,选项2是不可取的,因为它会污染您的代码并且不会产生任何其他收益.


Ale*_*lex 17

isDebugEnabled()通过连接字符串来构建日志消息时使用保留:

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

但是,在您的示例中,没有速度增益,因为您只是记录字符串而不执行连接等操作.因此,您只是在代码中添加膨胀并使其更难阅读.

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

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

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

请注意,尽管大多数日志记录API都提供了开箱即用的格式:例如,slf4j提供了以下内容:

logger.debug("My var is {}", myVar);
Run Code Online (Sandbox Code Playgroud)

这更容易阅读.

  • 使用String.format(...),同时使日志行更容易阅读,实际上可能会以一种糟糕的方式影响性能.SLF4J执行此操作的方式将参数发送到logger.debug方法,并且在构造字符串之前完成isDebugEnabled的评估.使用String.format(...)的方式,在完成对logger.debug的方法调用之前,将构造字符串,这样即使调试级别为,也将支付字符串构建的代价.没有启用.对不起挑选,只是为了避免新手混淆;-) (4认同)
  • String.format 比 concat 慢 40 倍,slf4j 有 2 个参数的限制 请参阅此处的数字:/sf/ask/64779641/ string-concatenation-in-java#925444 我见过很多分析器图,当生产系统在 INFO 或 ERROR 日志级别运行时,格式操作浪费在调试语句中 (2认同)

cyb*_*aek 9

在Java 8中,您不必使用它isDebugEnabled()来提高性能.

https://logging.apache.org/log4j/2.0/manual/api.html#Java_8_lambda_support_for_lazy_logging

import java.util.logging.Logger;
...
Logger.getLogger("hello").info(() -> "Hello " + name);
Run Code Online (Sandbox Code Playgroud)


And*_*arr 8

简短版本:你可以做布尔isDebugEnabled()检查.

理由:
1-如果复杂的逻辑/字符串连续.已添加到您的调试语句中,您已经有了检查.
2-您不必有选择地在"复杂"调试语句中包含该语句.所有陈述都包含在内.
3-在记录之前调用log.debug执行以下操作:

if(repository.isDisabled(Level.DEBUG_INT))
return;

这与调用日志基本相同.还是猫 isDebugEnabled().

然而!这就是log4j开发人员的想法(因为它是在他们的javadoc中,你可能应该选择它.)

这是方法

public
  boolean isDebugEnabled() {
     if(repository.isDisabled( Level.DEBUG_INT))
      return false;
    return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
  }
Run Code Online (Sandbox Code Playgroud)

这是它的javadoc

/**
*  Check whether this category is enabled for the <code>DEBUG</code>
*  Level.
*
*  <p> This function is intended to lessen the computational cost of
*  disabled log debug statements.
*
*  <p> For some <code>cat</code> Category object, when you write,
*  <pre>
*      cat.debug("This is entry number: " + i );
*  </pre>
*
*  <p>You incur the cost constructing the message, concatenatiion in
*  this case, regardless of whether the message is logged or not.
*
*  <p>If you are worried about speed, then you should write
*  <pre>
*    if(cat.isDebugEnabled()) {
*      cat.debug("This is entry number: " + i );
*    }
*  </pre>
*
*  <p>This way you will not incur the cost of parameter
*  construction if debugging is disabled for <code>cat</code>. On
*  the other hand, if the <code>cat</code> is debug enabled, you
*  will incur the cost of evaluating whether the category is debug
*  enabled twice. Once in <code>isDebugEnabled</code> and once in
*  the <code>debug</code>.  This is an insignificant overhead
*  since evaluating a category takes about 1%% of the time it
*  takes to actually log.
*
*  @return boolean - <code>true</code> if this category is debug
*  enabled, <code>false</code> otherwise.
*   */
Run Code Online (Sandbox Code Playgroud)

  • 感谢您提供 JavaDoc。我知道我以前在某处看到过这个建议,并试图找到一个明确的参考。即使这不是决定性的,至少也是非常消息灵通的。 (2认同)

Sat*_*hya 6

选项2更好.

本身并不能提高性能.但它确保性能不会降低.这是如何做.

通常我们期望logger.debug(someString);

但通常情况下,随着应用程序的增长,改变很多人,特别是新手开发人员,你可以看到

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

等等.

即使将日志级别设置为ERROR或FATAL,也会发生字符串串联!如果应用程序包含大量带有字符串连接的DEBUG级别消息,那么它肯定会受到性能影响,尤其是jdk 1.4或更低版本.(我不确定jdk internall的更高版本是否会执行任何stringbuffer.append()).

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


Pab*_*jim 6

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

值得注意的是,通过使用Simple Logging Facade for Java或(SLF4J) - http://www.slf4j.org/manual.html可以避免此问题.这允许方法调用,例如:

logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
Run Code Online (Sandbox Code Playgroud)

如果启用了调试,这只会将传入的参数转换为字符串.SLF4J顾名思义只是一个外观,日志调用可以传递给log4j.

您也可以非常轻松地"推出自己的"版本.

希望这可以帮助.