Java日志规范

       前言

一个在生产环境里运行的程序若是没有日志是很让维护者提心吊胆的,有太多杂乱又无心义的日志也是使人伤神。程序出现问题时候,从日志里若是发现不了问题可能的缘由是很使人受挫的。本文想讨论的是如何在Java程序里写好日志。html

通常来讲日志分为两种:业务日志和异常日志,使用日志咱们但愿能达到如下目标:java

  1. 对程序运行状况的记录和监控;
  2. 在必要时可详细了解程序内部的运行状态;
  3. 对系统性能的影响尽可能小

    Java日志框架

    Java的日志框架太多了。。。apache

    1. Log4j 或 Log4j 2 - Apache的开源项目,经过使用Log4j,咱们能够控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;用户也能够控制每一条日志的输出格式;经过定义每一条日志信息的级别,用户可以更加细致地控制日志的生成过程。这些能够经过一个配置文件(XML或Properties文件)来灵活地进行配置,而不须要修改程序代码。Log4j 2则是前任的一个升级,参考了Logback的许多特性;
    2. Logback - Logback是由log4j创始人设计的又一个开源日记组件。logback当前分红三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你能够很方便地更换成其它日记系统如log4j或JDK14 Logging;
    3. java.util.logging - JDK内置的日志接口和实现,功能比较简;
    4. Slf4j - SLF4J是为各类Logging API提供一个简单统一的接口),从而使用户可以在部署的时候配置本身但愿的Logging API实现;
    5. Apache Commons Logging - Apache Commons Logging (JCL)但愿解决的问题和Slf4j相似。

    选项太多了的后果就是选择困难症,个人见解是没有最好的,只有最合适的。在比较关注性能的地方,选择Logback或本身实现高性能Logging API可能更合适;在已经使用了Log4j的项目中,若是没有发现问题,继续使用多是更合适的方式;我通常会在项目里选择使用Slf4j, 若是不想有依赖则使用java.util.logging或框架容器已经提供的日志接口。api

    3.Java日志最佳实践

    定义日志变量

    日志变量每每不变,最好定义成final static,变量名用大写缓存

    日志分级

    Java的日志框架通常会提供如下日志级别,缺省打开info级别,也就是debug,trace级别的日志在生产环境不会输出,在开发和测试环境能够经过不一样的日志配置文件打开debug级别。服务器

    1. fatal - 严重的,形成服务中断的错误;
    2. error - 其余错误运行期错误;
    3. warn - 警告信息,如程序调用了一个即将做废的接口,接口的不当使用,运行状态不是指望的但仍可继续处理等;
    4. info - 有意义的事件信息,如程序启动,关闭事件,收到请求事件等;
    5. debug - 调试信息,可记录详细的业务处理到哪一步了,以及当前的变量状态;
    6. trace - 更详细的跟踪信息;

    在程序里要合理使用日志分级:oracle

     1 LOGGER.debug("entering getting content");
     2 String content =CacheManager.getCachedContent();
     3 if(content == null){
     4 
     5         //使用warn,由于程序还能够继续执行,但相似警告太多可能说明缓存服务不可用了,值得注意
     6         LOGGER.warn("Got empty content from cache,need perform database lookup");
     7 
     8         Connection conn = ConnectionFactory.getConnection();
     9         if (conn=null) {
    10         LOGGER.error("Can't get database connection, failed to return content");//尽可能提供详细的信息,知道错误的缘由,而不能简单的写logger.error("failed")       
    11         }else{
    12           try{    
    13            content = conn.query(...);    
    14          }catch ( IOException e ){
    15                        //异常要记录错误堆栈
    16                        LOGGER.error("Failed to perform database lookup", e );
    17                     }finally{
    18                        ConnectionFactory.releaseConnection(conn);
    19                      }
    20                }
    21 }  
    22 //调试的时候能够知道方法的返回了
    23 LOGGER.debug("returning content: "+ content);
    24 return content;

    基本的Logger编码规范

           1.在一个对象中一般只使用一个Logger对象,Logger应该是static final的,只有在少数须要在构造函数中传递logger的状况下才使用private final。框架

static final logger_LOG=loggerFactory.getLogger(Main.class);

 

            2.输出Exceptions的所有Throwable信息,由于logger.error(msg)和logger.error(msg,e.getMessage())这样的日志输出方法会丢失掉最重要的StackTrace信息。函数

void foo(){
     try {
            //  do something...
      }catch ( Exception e ){
          _LOG.error(e.getMessage()); // 错误
          _LOG.error("Bad things : ",e.getMessage()); // 错误
          _LOG.error("Bad things : ",e); // 正确
      }
}

             3.不容许记录日志后又抛出异常,由于这样会屡次记录日志,只容许记录一第二天志。性能

void foo() throws LogException{
     try{
         // do something...
     }catch ( Exception e ){
          _LOG.error("Bad things : ", e);
          throw new LogException("Bad things : ",e);
       }
}

            4.不容许出现System print(包括System.out.println和System.error.println)语句。

void foo() {
        try{
               // do something...
        }catch( Exception e ){ 
             System.out.println(e.getMessage()); // 错误
             System.err.println(e.getMessage()); // 错误
             _LOG.error("Bad things : ",e );  // 正确
          }
 }

            5.不容许出现printStackTrace。

void foo() {
     try {
             // do something...
     }catch ( Exception e ) {
            e.printStackTrace(); // 错误
            _LOG.error("Bad things : ", e ); //正确
         }
}

           6.日志性能的考虑,若是代码为核心代码,执行频率很是高,则输出日志建议增长判断,尤为是低级别的输出<debug、info、warn>。

debug日志太多后可能会影响性能,有一种改进方法是:

if (LOGGER.isDebugEnabled ()) {
    LOGGER.debug("returning content: "+ content);
}

但更好的方法是Slf4j提供的最佳实践:

LOGGER.debug("returning content: {}", content);

一方面能够减小参数构造的开销,另外一方面也不用多写两行代码。

7.有意义的日志

一般状况下在程序日志里记录一些比较有意义的状态数据:程序启动,退出的时间点;程序运行消耗时间;耗时程序的执行进度;重要变量的状态变化。

除此以外,在公共的日志里规避打印程序的调试或者提示信息。

 

FAQ:参考

1.7 Good Rules to Log Exceptions

2.5 common log mistakes

3.Java程序的日志

相关文章
相关标签/搜索