zoukankan      html  css  js  c++  java
  • Java编码常见的Log日志打印问题

    前言

        本文总结了作者在Java代码检视中遇到的一些关于日志打印的问题,并给出修改建议。因能力有限,难免存在错漏,欢迎指正。

    . 不规范的异常打印

        使用slf4j日志组件时,logger.error(与log.warn)接受Throwable参数,以打印异常名和详细的堆栈信息(可能内部调用e.printStackTrace())。

        但书写打印语句时,需要注意格式。例如:

    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"(过于简陋)。

        可使用如下正则表达式排查Java代码里不规范的异常打印:

    ^s*[Ll][Oo][Gg](ger|GER)*.(error|warn)("(.+?)"s*+s*(e|ex|e.getMessage())s*);.*

        该正则表达式可排查出形如上文b、c句的打印缺陷。考虑到实际代码书写风格的差异,会存在少量的漏判和误判。此外应注意,某些异常(如SQL或IO异常)可能泄露敏感信息,打印异常堆栈之前应根据网络安全要求做必要的”加工”。

    二. 不规范的变量打印

        使用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()适用。

        可使用如下正则表达式排查Java代码里不规范的变量打印:

    ^s*[Ll][Oo][Gg](ger|GER)*.[a-zA-Z]{4,5}("([^({})]+?)"s*\,s*([^es]|w{2,})s*);.*

        考虑到实际代码书写风格的差异,该正则表达式会存在漏判和误判。例如,"logger.error("Best print: ", ex);"会被误判。

    . 余的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级。

  • 相关阅读:
    R 读取xls/xlsx文件
    网页免费转换为可编辑的PDF
    Python: NumPy, Pandas学习资料
    鱼油资料
    Activity的四种启动模式和onNewIntent()
    Android Service、IntentService,Service和组件间通信
    Activity生命周期
    Node.js学习起步
    Android 技能图谱学习路线
    Blog
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/8447077.html
Copyright © 2011-2022 走看看