一、异常信息的格式规范问题
1 logger.error("Best print: ", e);
2 logger.error("Good print: {}", e); //a.
3 logger.error("Bad print: " + e); //b. 或 + e.toString()
4 logger.error("Bad print: " + e.getMessage()); //c. 或: {}", e.getMessage())
解释:
- a句仍可打印异常名和堆栈信息,但多输出一对花括号"{}";
- b句仅打印异常名;
- c句打印异常消息字符串
以空指针异常(Runtime异常)为例,b句打印"java.lang.NullPointerException",c句打印"null"(过于简陋)。
二、不规范的变量打印
使用slf4j日志组件打印变量时,建议使用”{}”占位符风格(C风格),而不是”+”拼接符(C++风格)。例如:
1 logger.error("Country: {}, Province: {}, City: {}", ctry, prov, city); //占位符
2 logger.error("Country: "+ctry+", Province:" +prov +", City: "+city); //拼接符
但最常见的变量打印问题,是打印时未使用占位符或拼接符:
1 logger.error("print cannotBePrinted: ", cannotBePrinted); //Wrong
2 logger.error("print canBePrinted: " + canBePrinted); //Good
3 logger.error("print canBePrinted: {}", canBePrinted); //Better
注意:
cannotBePrinted或canBePrinted为非Throwable的变量。即异常变量e不适用上述规则,但e.getMessage()适用。
三、多余的DEBUG级别判断
使用slf4j日志组件以Debug级别打印时,debug()方法内部会调用isDebugEnabled()判断调试级别。因此,下述代码里的判断没有必要
if(logger.isDebugEnabled())
{
logger.debug("handleChanges end:{}:{}(ms) ", new Object[] {changes, useTime});
}
四. 日志对象logger的声明
一般建议日志对象logger声明为private static final。声明为private可防止logger对象被其他类非法使用。声明为static可防止重复new出logger对象,造成资源浪费;还可以防止logger被序列化,造成安全风险。声明为final是因为在类的生命周期内无需变更logger。
然而,实际编码中可根据使用场景稍作变通。例如,static final成员变量通常要求大写(如LOGGER),如果觉得大写别扭,可以去除final修饰。又如,期望logger对象可复用时,可如下定义:
protected final Logger logger = LoggerFactory.getLogger(this.getClass());//????
这样,子类可以直接使用继承来的logger对象打印输出,而无需再次getLogger()获取日志对象。
五、日志级别应合理
如果日志不分级别或级别不合理,则定位问题时就无法快速有效地屏蔽大量低级别信息,给快速定位带来难度。建议与具体实现有关的日志使用debug级,一般的业务处理日志用info级,不影响业务进行的错误用warn级,而记录异常或重要错误的日志应为error级。