0. 问题
在很多框架中,我们看到在logger.debug处经常会这样写
if (logger.isDebugEnabled()) {
logger.debug(message);
}
我们知道logger.debug(),在日志级别不够的时候是不会输出日志的。那么这么写的目的何在?
1. 分析
我们来看一个例子
String error = "debug日志";
logger.debug("这是一个" + error);
按照正常的逻辑,在执行logger.debug()之前需要先行计算括号里的内容。然后才会判断当前日志级别不符合不输出。这里就存在一个无用计算的过程。相比对直接执行语句对logger.isDebugEnabled()进行先判断,显然是一个更优的方案,避免了无用的计算过程。
然而这个结论在占位符的使用下,有了一点不同。
再来看一个例子
1.1. logger.debug("这是一个{}", model);
public static class Model{
int age;
@Override
public String toString() {
System.out.println("toString called");
return "model{" +
"age=" + age +
'}';
}
}
public static void main(String[] args) {
Model model = new Model();
model.age = 3;
logger.debug("这是一个{}", model);
}
同样的作用,但是当日志级别高于debug时,不会执行括号内部的字符串拼接。也就是如果使用占位符{}来组合输出日志,可以不用判断logger.isDebugEnabled()
1.2. logger.debug("test:{}", test());
Model的toString方法在用占位符{}的方式时,没有执行,那么如果是一个普通的方法呢?
public static String test(){
System.out.println("test");
return "test";
}
public static void main(String[] args) {
logger.debug("test:{}", test()); //日志级别info
}
结果,logger.debug("test:{}", test());无输出,但是test()方法在控制台打印出了test。
结论:有占位符的存在,但是如果字符串拼接调用了函数,仍然会先执行函数,这和日志级别无关。
1.3. logger.debug("test2:{}, {}", model, test());
public static String test(){
System.out.println("print test");
return "fdsf";
}
public static void main(String[] args) {
Model model = new Model();
model.age = 3;
logger.debug("test1:" + model);
model.age = 5;
logger.debug("test2:{}, {}", model, test());
}
结果,最后一句debug,model的toString方法没有执行,而test()方法打印出了"print test"。也就是在占位符的作用下,对象的toString()方法不会执行,而不同方法仍然会执行。
2. 结论
通过logger.isDebugEnabled()进行先行判断,肯定是没有错的。虽然在占位符的帮助下,当日志级别高于debug时,对象的toString()方法不会执行,但是普通的方法仍然会执行。如果能够保证logger.debug()中的内容只涉及最简单的字符串拼接和toString(),那么可以简略logger.isDebugEnabled(),否则还是加上避免无用计算。