我在我的应用程序中使用 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()
方法两次:
我不认为如果我们在方法或类中编写多个logger.debug()
语句并根据选项1调用debug()
方法,那么与选项2相比,它是Log4J框架的开销。 {1}}是一种非常小的方法(就代码而言),它可能是内联的良好候选者。
答案 0 :(得分:217)
在这种特殊情况下,选项1更好。
当涉及调用各种对象的isDebugEnabled()
方法并连接结果时,保护语句(检查toString()
)可以防止日志消息的潜在昂贵计算。
在给定的示例中,日志消息是一个常量字符串,因此让记录器丢弃它与检查记录器是否已启用一样高效,并且由于分支较少,因此降低了代码的复杂性。
更好的是使用更新的日志记录框架,其中日志语句采用格式规范和由记录器替换的参数列表 - 但“懒惰”,仅当记录器已启用时。这是slf4j采取的方法。
有关更多信息,请参阅my answer to a related question,以及使用log4j执行此类操作的示例。
答案 1 :(得分:30)
由于在选项1中消息字符串是常量,因此使用条件包装日志记录语句绝对没有任何好处,相反,如果日志语句启用了调试,则您将在{ {1}}方法和isDebugEnabled()
方法中的一次。调用debug()
的成本约为5到30纳秒,对于大多数实际目的而言,这应该可以忽略不计。因此,选项2是不可取的,因为它会污染您的代码并且不会产生任何其他收益。
答案 2 :(得分:17)
通过连接字符串来构建日志消息时,保留使用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);
更容易阅读。
答案 3 :(得分:8)
简短版本:您也可以执行布尔值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 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.
* */
答案 4 :(得分:7)
在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);
答案 5 :(得分:6)
选项2更好。
本身并没有提高性能。但它确保性能不会降低。这是怎么回事。
通常我们期待 logger.debug(someString);
但通常,随着应用程序的增长,改变很多人,特别是新手开发人员,你可以看到
logger.debug(str1 + str2 + str3 + str4);
之类的。
即使将日志级别设置为ERROR或FATAL,也会发生字符串串联! 如果应用程序包含大量带有字符串连接的DEBUG级别消息,那么它肯定会受到性能影响,尤其是jdk 1.4或更低版本。 (我不确定jdk internall的更高版本是否会执行任何stringbuffer.append())。
这就是为什么选项2是安全的。即使字符串连接也不会发生。
答案 6 :(得分:6)
正如其他人所提到的,使用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。
您也可以非常轻松地“推出自己的”版本。
希望这有帮助。
答案 7 :(得分:3)
喜欢@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());
}
答案 8 :(得分:2)
它提高了速度,因为在调试文本中连接字符串是很常见的,例如:
boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
logger.debug("some debug text" + someState);
}
答案 9 :(得分:2)
对于单行,我在记录消息中使用了三元组,这样我就不会进行连接:
<强> 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());
}
}
答案 10 :(得分:2)
由于许多人在搜索log4j2时可能会查看此答案,并且几乎所有当前答案都不考虑log4j2或其中的最近更改,因此应该可以回答这个问题。
log4j2支持Supplier(目前是他们自己的实现,但根据文档计划在3.0版中使用Java的Supplier接口)。您可以在manual中阅读更多相关内容。这允许您将昂贵的日志消息创建放入供应商,该供应商仅在将要记录的情况下创建消息:
LogManager.getLogger().debug(() -> createExpensiveLogMessage());
答案 11 :(得分:1)
由于 Log4j 版本2.4
(或slf4j-api 2.0.0-alpha1
),最好使用fluent API(或Java 8 lambda support for lazy logging)来支持{{1} }作为日志消息参数,可以由 lambda 给出:
Supplier<?>
使用slf4j API进行操作:
log.debug("Debug message with expensive data : {}",
() -> doExpensiveCalculation());
答案 12 :(得分:0)
如果你使用选项2,你正在做一个快速的布尔检查。在选项一中,您正在进行方法调用(在堆栈上推送内容),然后执行仍然很快的布尔检查。我看到的问题是一致性。如果你的一些debug和info语句被包装而有些不是,那么它不是一致的代码风格。后来有人可以更改调试语句以包含连接字符串,这仍然非常快。我发现当我们在大型应用程序中包装debug和info语句并对其进行分析时,我们在性能上节省了几个百分点。不多,但足以让它值得工作。我现在在IntelliJ中设置了几个宏来自动为我生成包装的调试和信息语句。
答案 13 :(得分:0)
从2.x开始,Apache Log4j内置了此检查,因此不再需要SELECT page_id, ad_id, count() cnt
FROM TABLE_NAME
GROUP BY page_id, ad_id
ORDER BY page_id, cnt DESC
LIMIT 1 BY page_id
。只需执行isDebugEnabled()
,如果未启用,则会禁止显示消息。
答案 14 :(得分:-1)
我建议大多数人使用选项2作为事实,因为它不是非常昂贵。
案例1: log.debug(“一个字符串”)
情况2: log.debug(“one string”+“two string”+ object.toString + object2.toString)
在调用其中任何一个时,log.debug中的参数字符串(可以是CASE 1或Case2)都要进行评估。这就是每个人都意味着'昂贵'。如果您之前有条件'isDebugEnabled()',则不必评估这些是保存性能的位置。
答案 15 :(得分:-1)
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