Log Summary

日志,一个不太起眼的东西,很多人认为没有必要。因为你可以用 System.out.println() 来实现日志输出,但是这样的日志只会输出到 Console 而无法长期保存。

然而,真正接触生产环境以后才会发现原来日志是如此重要,如何写好日志方便排查问题同时尽量减小对性能的损耗成了本文的一个重要命题。

最佳实践示例

import org.slf4j.Logger;//#0
import org.slf4j.LoggerFactory;//#0

public class XxxFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(XxxFilter.class);//#0

    public String create(String openId, String sql) {
        LOGGER.debug("生成SQL: {}" + sql);//#1
        ...
        if (LOGGER.isDebugEnabled()) {
            BoundSql boundSql = statementHandler.getBoundSql();
            LOGGER.debug("生成SQL: {}" + boundSql.getSql());
        }//#2
        LOGGER.info("新增用户:{}", openId);//#3
        return "...";
    }
}

日志级别

  • debug
  • info
  • warn
  • error
  • fatal

级别选择

1.debug

这个级别最低,一般的来说,在系统实际运行过程中,一般都是不输出的。

因此这个级别的信息,可以随意的使用,任何觉得有利于在调试时更详细的了解系统运行状态的东东,比如变量的值等等,都输出来看看也无妨。

2.info

这个应该用来反馈系统的当前状态,所以,在这里输出的信息应该对最终用户具有实际意义,也就是最终用户要能够看得明白是什么意思才行。

从某种角度上说,info 输出的信息可以看作是软件产品的一部分(就像那些交互界面上的文字一样),所以需要谨慎对待,不可随便。

在理想情况下,INFO的日志信息要能让高级用户和系统管理员理解,并从日志信息中能知道系统当前的运行状态。比如对于一个机票预订系统来说,当一个用户完成一个机票预订操作之后,提醒应该给出“谁预订了从A到B的机票"。另一个需要输出INFO信息的地方就是一个系统操作引起系统的状态发生了重大变化(比如数据库更新,过多的系统请求)。

3.warn

所谓警告,应该是这个时候进行一些修复性的工作,应该还可以把系统恢复到正常状态中来,系统应该可以继续运行下去。

系统能继续运行,但是必须引起关注。对于存在的问题一般可以分为两类:一种系统存在明显的问题(比如,数据不可用);另一种就是系统存在潜在的问题,需要引起注意或者给出一些建议(比如,系统运行在安全模式或者访问当前系统的账号存在安全隐患)。总之就是系统仍然可用, 但是最好进行检查和调整

4.error

所谓错误,就是说可以进行一些修复性的工作,但无法确定系统会正常的工作下去,系统在以后的某个阶段,很可能会因为当前的这个问题,导致一个无法修复的错误(例如宕机),但也可能一直工作到停止也不出现严重问题。

系统发生了严重的错误,必须马上进行处理,否则系统将无法继续运行。比如NPE, 数据库不可用等

5.fatal

所谓fatal,那就是相当严重的了,可以肯定这种错误已经无法修复,并且如果系统继续运行下去的话,可以肯定必然会越来越乱。这时候采取的最好的措施不是试图将系统状态恢复到正常,而是尽可能地保留系统有效数据并停止运行。

性能优化

  • 本地调试日志级别为debug,详见logback.xml;生产环境为warn
  • 务必使用示例代码 #0 的形式声明日志
  • 不消耗性能的日志可以直接采用 #1 的形式,但是消耗性能的日志输出请先做判断(如#2),主要针对debug级别的日志
  • 日志信息中不要包含下列内容: 文件名;类名;代码行号;密码
  • 不要用日志输出异常

举例(只有G和L是正确用法)

try {
    Integer x = null;
    ++x;
} catch (Exception e) {
    log.error(e);        //A
    log.error(e, e);        //B
    log.error("" + e);        //C
    log.error(e.toString());        //D
    log.error(e.getMessage());        //E
    log.error(null, e);        //F
    log.error("", e);        //G
    log.error("{}", e);        //H
    log.error("{}", e.getMessage());        //I
    log.error("Error reading configuration file: " + e);        //J
    log.error("Error reading configuration file: " + e.getMessage());        //K
    log.error("Error reading configuration file", e);        //L
}

参考资料

  1. 正确使用日志的10个技巧
  2. 55最佳实践系列:Logging最佳实践
  3. Logging 日志记录最佳实践
2014-01-04 10:50702debuglog