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

发布于 2024-07-23 14:21:19 字数 959 浏览 3 评论 0原文

我在应用程序中使用 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() 方法两次:

  1. 在 debugEnabled 变量,
  2. 实际上由 logger.debug() 方法调用。

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

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

Option 1:

logger.debug("some debug text");

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

Option 2:

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

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

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:

  1. In case of assigning value to debugEnabled variable, and
  2. Actually called by logger.debug() method.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

-小熊_ 2024-07-30 14:21:19

对于单行,我在日志消息中使用三元,这样我就不会进行连接:

ej:

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

我做:

logger.debug(logger.isDebugEnable()?str1 + str2 + str3 + str4:null);

但是对于多行代码

ej.

for(Message mess:list) {
    logger.debug("mess:" + mess.getText());
}

我愿意:

if(logger.isDebugEnable()) {
    for(Message mess:list) {
         logger.debug("mess:" + mess.getText());
    }
}

For a single line, I use a ternary inside of logging message, In this way I don't do the concatenation:

ej:

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

I do:

logger.debug(logger.isDebugEnable()?str1 + str2 + str3 + str4:null);

But for multiple lines of code

ej.

for(Message mess:list) {
    logger.debug("mess:" + mess.getText());
}

I do:

if(logger.isDebugEnable()) {
    for(Message mess:list) {
         logger.debug("mess:" + mess.getText());
    }
}
段念尘 2024-07-30 14:21:19

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

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.

烏雲後面有陽光 2024-07-30 14:21:19

由于许多人在搜索 log4j2 时可能会查看此答案,并且几乎所有当前答案都不考虑 log4j2 或其中最近的更改,因此这应该有望回答该问题。

log4j2 支持 供应商< /a>s(目前是他们自己的实现,但根据文档,计划在 3.0 版本中使用 Java 的供应商接口)。 您可以在手册中阅读更多相关信息。 这允许您将昂贵的日志消息创建放入供应商中,该供应商仅在要记录消息时才创建消息:

LogManager.getLogger().debug(() -> createExpensiveLogMessage());

Since many people are probably viewing this answer when searching for log4j2 and nearly all current answers do not consider log4j2 or recent changes in it, this should hopefully answer the question.

log4j2 supports Suppliers (currently their own implementation, but according to the documentation it is planned to use Java's Supplier interface in version 3.0). You can read a little bit more about this in the manual. This allows you to put expensive log message creation into a supplier which only creates the message if it is going to be logged:

LogManager.getLogger().debug(() -> createExpensiveLogMessage());
蓝眸 2024-07-30 14:21:19

Log4j2 允许您将参数格式化为消息模板,类似于 String.format() ,从而无需执行 isDebugEnabled() 。

Logger log = LogManager.getFormatterLogger(getClass());
log.debug("Some message [myField=%s]", myField);

简单 log4j2.properties 示例:

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d %-5p: %c - %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = debug
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT

Log4j2 lets you format parameters into a message template, similar to String.format(), thus doing away with the need to do isDebugEnabled().

Logger log = LogManager.getFormatterLogger(getClass());
log.debug("Some message [myField=%s]", myField);

Sample simple log4j2.properties:

filter.threshold.type = ThresholdFilter
filter.threshold.level = debug
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d %-5p: %c - %m%n
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = debug
rootLogger.level = info
rootLogger.appenderRef.stdout.ref = STDOUT
你爱我像她 2024-07-30 14:21:19

Log4j 版本 2.4 (或slf4j-api 2.0.0-alpha1)开始,最好使用 Fluent API (或 Java 8 lambda 支持延迟日志记录),支持 Supplier 作为日志消息参数,可以通过 给出lambda

log.debug("Debug message with expensive data : {}", 
           () -> doExpensiveCalculation());

或与 slf4j API:

log.atDebug()
            .addArgument(() -> doExpensiveCalculation())
            .log("Debug message with expensive data : {}");

Since Log4j version 2.4 (orslf4j-api 2.0.0-alpha1) it's much better to use fluent API (or Java 8 lambda support for lazy logging), supporting Supplier<?> for log message argument, that can be given by lambda:

log.debug("Debug message with expensive data : {}", 
           () -> doExpensiveCalculation());

OR with slf4j API:

log.atDebug()
            .addArgument(() -> doExpensiveCalculation())
            .log("Debug message with expensive data : {}");
并安 2024-07-30 14:21:19

我建议大多数人事实上使用选项 2,因为它并不是非常昂贵。

情况1:
log.debug("一个字符串")

案例2:
log.debug("one string" + "two string" + object.toString + object2.toString)

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

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

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

Case2:
log.debug("one string" + "two string" + 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.

梦晓ヶ微光ヅ倾城 2024-07-30 14:21:19

从 2.x 开始,Apache Log4j 内置了此检查,因此不再需要 isDebugEnabled() 。 只需执行 debug(),如果未启用,消息将被抑制。

As of 2.x, Apache Log4j has this check built in, so having isDebugEnabled() isn't necessary anymore. Just do a debug() and the messages will be suppressed if not enabled.

深海夜未眠 2024-07-30 14:21:19

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

当涉及调用各种对象的 toString() 方法并连接结果。

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

更好的是使用更新的日志框架,其中日志语句采用格式规范和要由记录器替换的参数列表,但只有在启用记录器时才“惰性”。 这是 slf4j

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

In this particular case, Option 1 is better.

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.

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.

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

驱逐舰岛风号 2024-07-30 14:21:19

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

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.

山有枢 2024-07-30 14:21:19

使用 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);

这甚至更易于阅读。

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

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.

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

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:

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

which is even easier to read.

草莓酥 2024-07-30 14:21:19

在 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);

In Java 8, you don't have to use isDebugEnabled() to improve the performance.

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);
堇色安年 2024-07-30 14:21:19

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

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

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

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

您也可以非常轻松地“推出您自己的”版本。

希望这可以帮助。

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.

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:

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.

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

Hope this helps.

无尽的现实 2024-07-30 14:21:19

简短版本:您也可以执行布尔 isDebugEnabled() 检查。

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

if(repository.isDisabled(Level.DEBUG_INT))
返回;

这和调用log基本是一样的。 或猫。 isDebugEnabled()。

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

这是方法

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

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

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

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:

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

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

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

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

/**
*  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.
*   */
沉默的熊 2024-07-30 14:21:19

选项 2 更好。

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

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

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

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

等等。

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

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

Option 2 is better.

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

Normally we expect
logger.debug(someString);

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

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()).

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

孤星 2024-07-30 14:21:19

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

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

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);
}
蝶…霜飞 2024-07-30 14:21:19

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

Like @erickson it depends. If I recall, isDebugEnabled is 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.

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());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文