我在我的应用程序中使用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()方法两次:
如果将值分配给debugEnabled变量,和
实际上由logger.debug()方法调用。
我不认为如果我们在方法或类中编写多个logger.debug()语句并根据选项1调用debug()方法,那么与选项2相比,Log4J框架的开销是很大的。由于isDebugEnabled()是一个非常小的方法(就代码而言),因此它可能是内联的良好候选方法。
选择2更好。
它本身并不能提高性能。但它确保性能不会下降。这是如何。
通常我们认为
logger.debug (someString);
但通常,随着应用程序的发展,会有很多人转手,尤其是新手开发人员
logger.debug(str1 + str2 + str3 + str4);
诸如此类。
即使日志级别设置为ERROR或FATAL,字符串的连接也会发生!
如果应用程序包含大量带有字符串连接的DEBUG级别消息,那么它肯定会受到性能影响,特别是使用jdk 1.4或以下版本时。(我不确定以后版本的jdk内部是否做任何stringbuffer.append())。
这就是为什么选择2是安全的。即使字符串连接也不会发生。
使用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);
这样就更容易阅读了。
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.
比如@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());
}
正如其他人提到的,只有在创建字符串是一个耗时的调用时,使用guard语句才真正有用。具体的例子是当创建字符串时会触发一些延迟加载。
值得注意的是,这个问题可以通过使用Java的简单日志Facade或(SLF4J) - http://www.slf4j.org/manual.html来完成避免。这允许调用如下方法:
logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
这只会在启用调试时将传入的参数转换为字符串。SLF4J顾名思义只是一个facade,日志调用可以传递给log4j。
你也可以很容易地“滚动”你自己的版本。
希望这能有所帮助。
简短版本:你也可以做布尔isDebugEnabled()检查。
原因:
1-如果复杂的逻辑/字符串连接。添加到调试语句时,则检查已经就位。
2-你不必有选择地在“复杂”调试语句中包含语句。所有语句都是这样包含的。
3-在记录日志之前,调用log.debug执行以下命令:
如果(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.
* */
对于单行,我在日志消息中使用了三元,这样我就不做拼接:
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());
}
}
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