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办法外部,会判断日志级别,如果利用级别大于日志级别,就不会输入日志。以下是isDebugEnableddebug的外围代码(我删除了一些无关代码):

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());}