Java 在 log4j 中,在记录之前检查 isDebugEnabled 是否可以提高性能?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/963492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 21:38:30  来源:igfitidea点击:

In log4j, does checking isDebugEnabled before logging improve performance?

javalogginglog4j

提问by Silent Warrior

I am using Log4Jin my application for logging. Previously I was using debug call like:

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

Option 1:

选项1:

logger.debug("some debug text");

but some links suggest that it is better to check isDebugEnabled()first, like:

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

Option 2:

选项 2:

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

So my question is "Does option 2 improve performance any way?".

所以我的问题是“选项 2 是否会以任何方式提高性能?”。

Because in any case Log4J framework have same check for debugEnabled. For option 2 it might be beneficial if we are using multiple debug statement in single method or class, where the framework does not need to call isDebugEnabled()method multiple times (on each call); in this case it calls isDebugEnabled()method only once, and if Log4J is configured to debug level then actually it calls isDebugEnabled()method twice:

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

  1. In case of assigning value to debugEnabled variable, and
  2. Actually called by logger.debug() method.
  1. 在为 debugEnabled 变量赋值的情况下,以及
  2. 实际上由 logger.debug() 方法调用。

I don't think that if we write multiple logger.debug()statement in method or class and calling debug()method according to option 1 then it is overhead for Log4J framework in comparison with option 2. Since isDebugEnabled()is a very small method (in terms of code), it might be good candidate for inlining.

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

采纳答案by erickson

In this particular case, Option 1 is better.

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

The guard statement (checking isDebugEnabled()) is there to prevent potentially expensive computation of the log message when it involves invocation of the toString()methods of various objects and concatenating the results.

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

In the given example, the log message is a constant string, so letting the logger discard it is just as efficient as checking whether the logger is enabled, and it lowers the complexity of the code because there are fewer branches.

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

Better yet is to use a more up-to-date logging framework where the log statements take a format specification and a list of arguments to be substituted by the logger—but "lazily," only if the logger is enabled. This is the approach taken by slf4j.

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

See my answer to a related questionfor more information, and an example of doing something like this with log4j.

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

回答by Tom

It improves the speed because it's common to concatenate strings in the debug text which is expensive eg:

它提高了速度,因为在调试文本中连接字符串很常见,这很昂贵,例如:

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

回答by Sathya

Option 2 is better.

选项 2 更好。

Per se it does not improve performance. But it ensures performance does not degrade. Here's how.

它本身并没有提高性能。但它确保性能不会降低。就是这样。

Normally we expect logger.debug(someString);

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

But usually, as the application grows, changes many hands, esp novice developers, you could see

但通常,随着应用程序的增长,易手,尤其是新手开发人员,您可以看到

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

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

and the like.

之类的。

Even if log level is set to ERROR or FATAL, the concatenation of strings do happen ! If the application contains lots of DEBUG level messages with string concatenations, then it certainly takes a performance hit especially with jdk 1.4 or below. (Iam not sure if later versions of jdk internall do any stringbuffer.append()).

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

Thats why Option 2 is safe. Even the string concatenations dont happen.

这就是为什么选项 2 是安全的。甚至字符串连接也不会发生。

回答by Alex

Using the isDebugEnabled()is reserved for when you're building up log messages by concatenating Strings:

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

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

However, in your example there is no speed gain as you're just logging a String and not performing operations such as concatenation. Therefore you're just adding bloat to your code and making it harder to read.

但是,在您的示例中,没有速度提升,因为您只是在记录一个 String 而没有执行诸如连接之类的操作。因此,您只是在代码中添加了膨胀并使其更难阅读。

I personally use the Java 1.5 format calls in the String class like this:

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

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

I doubt there's much optimisation but it's easier to read.

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

Do note though that most logging APIs offer formatting like this out of the box: slf4j for example provides the following:

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

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

which is even easier to read.

这更容易阅读。

回答by Ceki

Since in option 1 the message string is a constant, there is absolutely no gain in wrapping the logging statement with a condition, on the contrary, if the log statement is debug enabled, you will be evaluating twice, once in the isDebugEnabled()method and once in debug()method. The cost of invoking isDebugEnabled()is in the order of 5 to 30 nanoseconds which should be negligible for most practical purposes. Thus, option 2 is not desirable because it pollutes your code and provides no other gain.

由于在选项 1 中消息字符串是一个常量,因此用条件包装日志语句绝对没有好处,相反,如果日志语句启用了调试,您将评估两次,一次在isDebugEnabled()方法中,一次在debug()方法。调用的成本isDebugEnabled()大约为 5 到 30 纳秒,对于大多数实际用途来说,这应该可以忽略不计。因此,选项 2 是不可取的,因为它会污染您的代码并且不提供其他收益。

回答by Ceki

Like @erickson it depends. If I recall, isDebugEnabledis already build in the debug()method of Log4j.
As long as you're not doing some expensive computations in your debug statements, like loop on objects, perform computations and concatenate strings, you're fine in my opinion.

就像@erickson 一样,这取决于。如果我记得,isDebugEnabled是已经在debug()Log4j的方法中构建的。
只要您不在调试语句中进行一些昂贵的计算,例如对象循环、执行计算和连接字符串,我认为您就可以。

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

would be better as

会更好

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());
}

回答by Javamann

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,则您正在进行快速的布尔检查。在选项一中,您正在进行方法调用(将内容推入堆栈),然后进行布尔检查,这仍然很快。我看到的问题是一致性。如果您的某些调试和信息语句被包装,而有些则不是,这不是一致的代码风格。另外,稍后有人可以更改调试语句以包含连接字符串,这仍然非常快。我发现,当我们在大型应用程序中包装调试和信息语句并对其进行分析时,我们节省了几个百分点的性能。不多,但足以让它值得工作。我现在在 IntelliJ 中设置了几个宏来自动为我生成包装的调试和信息语句。

回答by Pablojim

As others have mentioned using the guard statement is only really useful if creating the string is a time consuming call. Specific examples of this are when creating the string will trigger some lazy loading.

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

It is worth noting that this problem can be completed avoided by using Simple Logging Facade for Java or (SLF4J) - http://www.slf4j.org/manual.html. This allows method calls such as:

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

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

This will only convert the passed in parameters to strings if debug is enabled. SLF4J as its name suggests is only a facade and the logging calls can be passed to log4j.

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

You could also very easily "roll your own" version of this.

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

Hope this helps.

希望这可以帮助。

回答by Andrew Carr

Short Version: You might as well do the boolean isDebugEnabled() check.

简短版本:您不妨进行布尔 isDebugEnabled() 检查。

Reasons:
1- If complicated logic / string concat. is added to your debug statement you will already have the check in place.
2- You don't have to selectively include the statement on "complex" debug statements. All statements are included that way.
3- Calling log.debug executes the following before logging:

原因:
1- 如果复杂的逻辑/字符串连接。添加到您的调试语句中,您将已经进行了检查。
2- 您不必选择性地包含“复杂”调试语句中的语句。所有语句都以这种方式包含在内。
3- 调用 log.debug 在登录之前执行以下操作:

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

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

This is basically the same as calling log. or cat. isDebugEnabled().

这与调用日志基本相同。或猫。isDebugEnabled()。

HOWEVER! This is what the log4j developers think (as it is in their javadoc and you should probably go by it.)

然而!这就是 log4j 开发人员的想法(因为它在他们的 javadoc 中,您可能应该通过它。)

This is the method

这是方法

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

This is the javadoc for it

这是它的 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.
*   */

回答by Jolly1234

I would recommend using Option 2 as de facto for most as it's not super expensive.

我建议大多数情况下实际上使用选项 2,因为它不是非常昂贵。

Case 1: log.debug("one string")

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

Case2: log.debug("one string" + "two string" + object.toString + object2.toString)

Case2: log.debug("一串" + "两串" + object.toString + object2.toString)

At the time either of these are called, the parameter string within log.debug (be it CASE 1 or Case2) HAS to be evaluated. That's what everyone means by 'expensive.' If you have a condition before it, 'isDebugEnabled()', these don't have to be evaluated which is where the performance is saved.

在调用其中任何一个时,必须评估 log.debug 中的参数字符串(无论是 CASE 1 还是 Case2)。这就是每个人所说的“昂贵”的意思。如果在它之前有一个条件“isDebugEnabled()”,则不必评估这些条件,即保存性能的位置。