在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()
方法:
- 在为debugEnabledvariables赋值的情况下,
- 实际上由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()
来提高性能。
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()”,则不必评估这些是保存性能的地方。