在log4j中,在logging提高性能之前检查isDebugEnabled吗?

我在我的应用程序中使用Log4J进行日志logging。 以前我使用debugging调用如:

选项1:

logger.debug("some debug text"); 

但是有些链接build议首先检查isDebugEnabled() ,比如:

选项2:

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

所以我的问题是“ 选项2以任何方式改善性能? ”。

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

  1. 在为debugEnabledvariables赋值的情况下,
  2. 实际上由logger.debug()方法调用。

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

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

guard语句(检查isDebugEnabled() )是为了防止在涉及调用各种对象的toString()方法并连接结果时,对日志消息进行可能昂贵的计算。

在给定的例子中,日志消息是一个常量string,所以让logging器丢弃它和检查logging器是否被启用一样高效,并且由于分支较less而降低了代码的复杂性。

更好的方法是使用更新的日志logging框架,其中日志语句采用格式规范和由logging器代替的参数列表,但只有在启用logging器时才会“懒洋洋地”。 这是slf4j采取的方法。

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

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

使用isDebugEnabled()是通过连接string来build立日志消息时保留的:

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

然而,在你的例子中,没有速度增益,因为你只是logging一个string,而不是执行诸如连接之类的操作。 因此,您只需将代码添加到代码中,并使其更难阅读。

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

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

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

请注意,尽pipe大多数日志loggingAPI都提供了这种开箱即用的格式:例如slf4j提供了以下内容:

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

这更容易阅读。

选项2更好。

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

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

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

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

等等。

即使日志级别设置为ERROR或FATAL,string连接也会发生! 如果应用程序包含大量带string连接的DEBUG级别的消息,那么它肯定会带来性能问题,尤其是在jdk 1.4或更低版本中。 (我不确定是否更高版本的jdk internall做任何stringbuffer.append())。

这就是为什么选项2是安全的。 即使是string连接也不会发生。

短版本:你也可以做布尔isDebugEnabled()检查。

原因:
1-如果复杂的逻辑/stringconcat。 被添加到你的debugging语句,你将已经有了检查。
2-您不必在“复杂”debugging语句中select性地包含该语句。 所有的陈述都是这样包含的。
3-调用log.debug在logging之前执行以下操作:

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

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

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

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

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

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

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

希望这可以帮助。

在Java8中,您不必使用isDebugEnabled()来提高性能。

https://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#finer-java.util.function.Supplier-

 import java.util.logging.Logger; ... Logger.getLogger(“hello”).info(()->”Hello “ + name); 

它提高了速度,因为在debugging文本中连接string是很常见的,例如:

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

像@erickson它取决于。 如果我记得, isDebugEnabled已经build立在Log4j的debug()方法中。
只要你不在debugging语句中做一些昂贵的计算,比如循环对象,执行计算和连接string,在我看来你没问题。

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

对于单行来说 ,我在日志消息里面使用了一个三元组,这样我就不用连接了:

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

如果你使用选项2,你正在做一个快速的布尔检查。 在选项一中,你正在做一个方法调用(把东西推到堆栈上),然后做一个布尔检查,它仍然很快。 我看到的问题是一致性。 如果你的一些debugging和信息语句被封装,有些则不是一致的代码风格。 再加上以后的人可以改变debugging语句,以包括连接string,这仍然是非常快。 我发现,当我们在大型应用程序中包装debugging和信息语句并进行configuration时,我们节省了几个百分点的性能。 不多,但足以让它值得工作。 我现在在IntelliJ中设置了一些macros,为我自动生成封装的debugging和信息语句。

我会build议使用选项2作为事实上的,因为它不是很昂贵。

情况1:log.debug(“一个string”)

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

在调用其中的任何一个时,log.debug中的参数string(无论是CASE 1还是Case2)都需要进行评估。 这就是大家所说的“昂贵”的意思。 如果之前有一个条件“isDebugEnabled()”,则不必评估这些是保存性能的地方。