我在我的应用程序中使用 Log4J 进行日志记录 . 以前我使用调试调用如:
Option 1:
logger.debug("some debug text");
但有些链接表明最好首先查看 isDebugEnabled()
,如:
Option 2:
boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
logger.debug("some debug text");
}
所以我的问题是“ Does option 2 improve performance any way? ” .
因为在任何情况下Log4J框架都对debugEnabled进行相同的检查 . 对于选项2,如果我们在单个方法或类中使用多个debug语句可能是有益的,其中框架不需要多次调用 isDebugEnabled()
方法(在每次调用时);在这种情况下,它只调用 isDebugEnabled()
方法一次,如果Log4J配置为调试级别,则实际上它调用 isDebugEnabled()
方法两次:
-
如果为debugEnabled变量赋值,和
-
实际上是由logger.debug()方法调用的 .
我不认为如果我们在方法或类中编写多个 logger.debug()
语句并根据选项1调用 debug()
方法,那么与选项2相比,它是Log4J框架的开销 . 因为 isDebugEnabled()
是一个非常小的方法(就代码而言) ,它可能是内联的好选择 .
15 回答
在这种特殊情况下,选项1更好 .
当涉及调用各种对象的
toString()
方法并连接结果时,保护语句(检查isDebugEnabled()
)用于防止日志消息的潜在昂贵计算 .在给定的示例中,日志消息是一个常量字符串,因此让 Logger 丢弃它与检查 Logger 是否已启用一样高效,并且由于分支较少,因此降低了代码的复杂性 .
更好的是使用更新的日志记录框架,其中日志语句采用格式规范和由 Logger 替换的参数列表 - 但仅当启用了 Logger 时"lazily," . 这是slf4j采取的方法 .
有关更多信息,请参阅my answer to a related question,以及使用log4j执行此类操作的示例 .
由于在选项1中消息字符串是常量,因此在使用条件包装日志语句时绝对没有任何好处,相反,如果日志语句是启用调试的,则您将在
isDebugEnabled()
方法中进行两次评估,一次在debug()
方法 . 调用isDebugEnabled()
的成本大约为5到30纳秒,对于大多数实际用途来说这应该可以忽略不计 . 因此,选项2是不可取的,因为它会污染您的代码并且不会产生任何其他收益 .当您通过连接字符串来构建日志消息时,保留使用
isDebugEnabled()
:但是,在您的示例中,没有速度增益,因为您只是记录字符串而不执行连接等操作 . 因此,您只是在代码中添加膨胀并使其更难阅读 .
我个人在String类中使用Java 1.5格式调用,如下所示:
我怀疑它有很多优化,但它更容易阅读 .
请注意,尽管大多数日志记录API都提供了开箱即用的格式:例如,slf4j提供了以下内容:
这更容易阅读 .
简短版本:你可以做布尔isDebugEnabled()检查 .
原因:
1-如果复杂的逻辑/字符串连接 . 已添加到您的调试语句中,您已经有了检查 .
2-您不必在"complex"调试语句中有选择地包含该语句 . 所有陈述都包含在内 .
3-在记录之前调用log.debug执行以下操作:
if(repository.isDisabled(Level.DEBUG_INT)) return;
这与调用日志基本相同 . 还是猫isDebugEnabled() .
然而!这就是log4j开发人员的想法(因为它是在他们的javadoc中,你可能应该选择它 . )
这是方法
这是它的javadoc
在Java 8中,您不必使用
isDebugEnabled()
来提高性能 .https://logging.apache.org/log4j/2.0/manual/api.html#Java_8_lambda_support_for_lazy_logging
选项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可以完成此问题 . 这允许方法调用,例如:
如果启用了调试,这只会将传入的参数转换为字符串 . SLF4J顾名思义只是一个外观,日志调用可以传递给log4j .
您也可以非常轻松地“推出自己的”版本 .
希望这可以帮助 .
就像@erickson一样,它取决于 . 如果我记得,
isDebugEnabled
已经在Log4j的debug()
方法中构建 .只要你认为我很好 .
会更好的
它提高了速度,因为在调试文本中连接字符串是很常见的,这很昂贵,例如:
对于 single line ,我在记录消息中使用了三元组,这样我就不进行连接:
ej:
I do:
但是对于 multiple lines 的代码
ej.
I do:
由于很多人在搜索log4j2时可能会查看此答案,并且几乎所有当前答案都不考虑log4j2或其中的最近更改,因此应该有希望回答这个问题 .
log4j2支持Supplier(目前是他们自己的实现,但根据文档计划在3.0版中使用Java的Supplier接口) . 您可以在manual中阅读更多相关内容 . 这允许您将昂贵的日志消息创建放入供应商,该供应商仅在将要记录的情况下创建消息:
如果使用选项2,则执行布尔检查很快 . 在选项一中,您正在进行方法调用(在堆栈上推送内容),然后执行仍然很快的布尔检查 . 我看到的问题是一致性 . 如果你的一些debug和info语句被包装而有些不是,那么它不是一致的代码风格 . 后来有人可以更改调试语句以包含连接字符串,这仍然非常快 . 我发现当我们在一个大型应用程序中包装debug和info语句并对其进行分析时,我们在性能上节省了几个百分点 . 不多,但足以让它值得工作 . 我现在在IntelliJ中设置了几个宏来自动为我生成包装的调试和信息语句 .
从2.x开始,Apache Log4j内置了此检查,因此不再需要
isDebugEnabled()
. 只需执行debug()
,如果未启用,将禁止显示消息 .Log4j2允许您将参数格式化为消息模板,类似于
String.format()
,从而无需执行isDebugEnabled()
.示例简单的log4j2.properties:
我建议大多数人使用选项2作为事实,因为它不是非常昂贵 .
案例1:log.debug(“一个字符串”)
Case2:log.debug(“one string”“two string”object.toString object2.toString)
在调用其中任何一个时,log.debug中的参数字符串(可以是CASE 1或Case2)都要进行评估 . 这就是每个人都意味着'昂贵' . 如果您之前有条件'isDebugEnabled()',则不必评估这些是保存性能的位置 .