共计 2825 个字符,预计需要花费 8 分钟才能阅读完成。
Java 日志框架中须要判断 log.isDebugEnabled()吗?
背景
在日常开发中,我的项目会应用形象日志接口 slf4j 来打印日志。如下是一段典型的打印日志代码:
logger.debug("hello, world");
然而在一些我的项目或第三方开源框架中,也会发现有些代码在输入日志时,在后面增加 if 判断,代码如下:
if(logger.isDebugEnabled()){logger.debug("hello, world");
}
简略来说,先应用 isDebufEnabled
来判断日志级别。这么写的目标是什么呢?而且网上有些文档指出这种判断其实是不须要的。那理论开发中,咱们到底要不要判断 isDebugEnabled
呢?心愿通过这篇文章来分享一些我对日志打印的思考。
简略的源码分析
有些人不明确为什么要增加 if 判断,会认为这样是为了管制日志的输入。其实这是不对的。对于上面的两段代码:
logger.debug("hello, world")
if(logger.isDebugEnabled()){logger.debug("hello, world");
}
如果利用的日志级别大于 debug
,比方为info
。那么这两段代码,最终都不会输入日志。在debug
办法外部,会判断日志级别,如果利用级别大于日志级别,就不会输入日志。以下是 isDebugEnabled
和debug
的外围代码(我删除了一些无关代码):
isDebugEnabled
办法:
public boolean isDebugEnabled(Marker marker) {final FilterReply decision = callTurboFilters(marker, Level.DEBUG);
if (decision == FilterReply.NEUTRAL) {return effectiveLevelInt <= Level.DEBUG_INT;} else {throw new IllegalStateException("Unknown FilterReply value:" + decision);
}
}
debug
办法:
private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,
final Throwable t) {final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t);
if (decision == FilterReply.NEUTRAL) {if (effectiveLevelInt > level.levelInt) {return;}
} else if (decision == FilterReply.DENY) {return;}
buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t);
}
那么问题来了,既然 isDebugEnabled
不会影响日志是否输入,那为什么要增加这个判断呢?
为什么要增加 if 判断
咱们思考上面几段代码:
代码清单一:
logger.debug("hello, world")
代码清单二:
if(logger.isDebugEnabled()){logger.debug("hello, world")
}
代码清单三:
logger.debug("hello" + "world");
代码清单四:
logger.debug(String.format("hello, %s", "world"))
代码清单五:
logger.debug("hello {}", "world");
以上 5 段代码,如果利用日志级别为 info,那么这 5 段代码都不会输入日志。它们的差别其实是 性能不同。上面咱们来一一剖析。
- 代码清单一:执行
debug
办法。debug
办法外部判断日志级别,而后退出。 - 代码清单二:
isDebugEnabled
判断日志级别,而后退出。 - 代码清单三:先拼接字符串“hello”和“world”。而后执行
debug
办法。 - 代码清单四:先执行
String.format
办法,而后执行debug
办法。 - 代码清单五:执行
debug
办法。
大家发现了吗?尽管最终都不会输入日志,但这 5 段代码还是有差别的。代码清单三和代码清单四别离执行了 ”+” 字符串拼接和 String.format
办法,但最终却没用到。也就是说,这两段代码执行了一些无用操作,造成了额定的性能损耗。
所以,代码清单二中增加 isDebugEnabled
能够防止无用的字符串操作,进步性能。
isDebugEnabled
是必须的吗?
上一节中,我提到应用 isDebugEnabled
能够晋升性能,那是不是在所有中央都须要增加 isDebugEnabled
判断呢?
先说论断,不是的。应该依据具体场景来判断是否增加isDebugEnabled
。
比方上面的代码:
logger.debug("hello, world");
打印一条日志“hello,world”。那么这时候就不必增加 isDebugEnabled
判断了。对于 isDebugEnabled
的应用场景,总结了如下的最佳实际:
isDebugEnabled
最佳实际
准则一:如果打印字符串常量,不须要isDebugEnabled
比拟上面两段代码:
logger.debug("hello, world");
if(logger.isDebugEnabled()){logger.debug("hello, world");
}
因为打印的日志是字面常量,没有计算逻辑。两段代码的性能是简直一样的。增加 isDebugEnabled
反而会导致额定的代码。
准则二:如果有参数,且参数只是字符串常量或计算简略,应用占位符
思考如下代码,debug
办法蕴含了参数 user.getName()
。尽管执行debug
办法时,会计算 user.getName()
,但只是一个简略的get 办法,没有简单计算,这时候,也能够不增加isDebugEnabled
。
logger.debug("hello, {}", user.getName());
准则三:如果有参数,且参数计算简单,增加isDebugEnabled
logger.debug("order price: {}", calculatePrice());
假如 calculatePrice
办法须要通过简单计算。那么就应该增加 isDebugEnabled
判断,应用如下的代码:
if(logger.isDebugEnabled()){logger.debug("order price: {}", calculatePrice());
}