我在我的应用程序中使用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()
在方法或类中编写多个语句并debug()
根据选项1 调用方法,那么与选项2相比,它是Log4J框架的开销.因为它isDebugEnabled()
是一个非常小的方法(就代码而言),它可能是内联的好人选.
在这种特殊情况下,选项1更好.
isDebugEnabled()
当涉及调用toString()
各种对象的方法并连接结果时,保护语句(检查)用于防止可能昂贵的日志消息计算.
在给定的示例中,日志消息是一个常量字符串,因此让记录器丢弃它与检查记录器是否已启用一样高效,并且由于分支较少,因此降低了代码的复杂性.
更好的是使用更新的日志记录框架,其中日志语句采用格式规范和由记录器替换的参数列表 - 但"懒惰",仅当记录器已启用时.这是slf4j采用的方法.
有关更多信息,请参阅我对相关问题的回答,以及使用log4j执行此类操作的示例.
由于在方案1的消息字符串是一个常数,也绝对与条件包裹logging语句,相反没有收获,如果日志语句启用调试,你将被评估两次,一次是在isDebugEnabled()
方法和一次debug()
方法.调用的成本isDebugEnabled()
大约为5到30纳秒,对于大多数实际目的而言应该可以忽略不计.因此,选项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);
这更容易阅读.
在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);
简短版本:你可以做布尔isDebugEnabled()检查.
理由:
1-如果复杂的逻辑/字符串连续.已添加到您的调试语句中,您已经有了检查.
2-您不必有选择地在"复杂"调试语句中包含该语句.所有陈述都包含在内.
3-在记录之前调用log.debug执行以下操作:
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 theDEBUG
* Level. * *This function is intended to lessen the computational cost of * disabled log debug statements. * *
For some
cat
Category object, when you write, ** cat.debug("This is entry number: " + i ); ** *You incur the cost constructing the message, concatenatiion in * this case, regardless of whether the message is logged or not. * *
If you are worried about speed, then you should write *
* if(cat.isDebugEnabled()) { * cat.debug("This is entry number: " + i ); * } ** *This way you will not incur the cost of parameter * construction if debugging is disabled for
cat
. On * the other hand, if thecat
is debug enabled, you * will incur the cost of evaluating whether the category is debug * enabled twice. Once inisDebugEnabled
and once in * thedebug
. This is an insignificant overhead * since evaluating a category takes about 1%% of the time it * takes to actually log. * * @return boolean -true
if this category is debug * enabled,false
otherwise. * */
选项2更好.
本身并不能提高性能.但它确保性能不会降低.这是如何做.
通常我们期望logger.debug(someString);
但通常情况下,随着应用程序的增长,改变很多人,特别是新手开发人员,你可以看到
logger.debug(str1 + str2 + str3 + str4);
等等.
即使将日志级别设置为ERROR或FATAL,也会发生字符串串联!如果应用程序包含大量带有字符串连接的DEBUG级别消息,那么它肯定会受到性能影响,尤其是jdk 1.4或更低版本.(我不确定jdk internall的更高版本是否会执行任何stringbuffer.append()).
这就是为什么选项2是安全的.即使字符串连接也不会发生.
正如其他人所提到的,使用guard语句只有在创建字符串是一个耗时的调用时才真正有用.这个的具体例子是在创建字符串时会触发一些延迟加载.
值得注意的是,通过使用Simple Logging Facade for Java或(SLF4J) - http://www.slf4j.org/manual.html可以避免此问题.这允许方法调用,例如:
logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
如果启用了调试,这只会将传入的参数转换为字符串.SLF4J顾名思义只是一个外观,日志调用可以传递给log4j.
您也可以非常轻松地"推出自己的"版本.
希望这可以帮助.