• 程序那些事:日志记录的作用和方法
    时间:2012-11-07   作者:冶秀刚   出处:infoq.com

    程序中记录日志一般有两个目的:Troubleshooting和显示程序运行状态。好的日志记录方式可以提供我们足够多定位问题的依据。日志记录大家都会认为简单,但如何通过日志可以高效定位问题并不是简单的事情。这里列举下面三个方面的内容,辅以代码示例,总结如何写好日志,希望对他人有所启发和帮助:1、怎样记日志可以方便Troubleshooting。 2、程序运行状态可以记哪些。3、应该避免怎样的日志方式。

    怎样记日志可以方便Troubleshooting?

    1. 对外部的调用封装

    程序中对外部系统与模块的依赖调用前后都记下日志,方便接口调试。出问题时也可以很快理清是哪块的问题

    1.  LOG.debug("Calling external system:" + parameters);  
    2.  Object result = null;  
    3.  try {  
    4.      result = callRemoteSystem(params);  
    5.      LOG.debug("Called successfully. result is " + result);  
    6.  } catch (Exception e) {  
    7.      LOG.warn("Failed at calling xxx system . exception : " + e);  
    8.  }  

    2.状态变化

    程序中重要的状态信息的变化应该记录下来,方便查问题时还原现场,推断程序运行过程

    1.  boolean isRunning;  
    2.    
    3.  isRunning = true;  
    4.  LOG.info("System is running");  
    5.    
    6.  //...  
    7.    
    8.  isRunning = false;  
    9.  LOG.info("System was interrupted by " + Thread.currentThread().getName());  

    3.系统入口与出口:

    这个粒度可以是重要方法级或模块级。记录它的输入与输出,方便定位

    1.  void execute(Object input) {  
    2.      LOG.debug("Invoke parames : " + input);  
    3.      Object result = null;  
    4.        
    5.      //business logic
    6.        
    7.      LOG.debug("Method result : " + result);  
    8.  }  

    4.业务异常:

    任何业务异常都应该记下来:

    1.  try {  
    2.      //business logical  
    3.  } catch (IOException e) {  
    4.      LOG.warn("Description xxx" , e);  
    5.  } catch (BusinessException e) {  
    6.      LOG.warn("Let me know anything");  
    7.  } catch (Exception e) {  
    8.      LOG.error("Description xxx", e);  
    9.  }  
    10.   

    5.非预期执行:

    为程序在“有可能”执行到的地方打印日志。如果我想删除一个文件,结果返回成功。但事实上,那个文件在你想删除之前就不存在了。最终结果是一致的,但程序得让我们知道这种情况,要查清为什么文件在删除之前就已经不存在

    1.  int myValue = xxxx;  
    2.  int absResult = Math.abs(myValue);  
    3.  if (absResult < 0) {  
    4.      LOG.info("Original int " + myValue + "has nagetive abs " + absResult);  
    5.  }  

    6.很少出现的else情况:

    else可能吞掉你的请求,或是赋予难以理解的最终结果

    1.  Object result = null;  
    2.  if (running) {  
    3.     result = xxx;  
    4.  } else {  
    5.     result = yyy;  
    6.     LOG.debug("System does not running, we change the final result");  
    7.  }  

    程序运行状态可以记哪些?

    程序在运行时就像一个机器人,我们可以从它的日志看出它正在做什么,是不是按预期的设计在做,所以这些正常的运行状态是要有的。

    1. 程序运行时间:

    1.  long startTime = System.currentTime();  
    2.    
    3.  // business logical  
    4.    
    5.  LOG.info("execution cost : " + (System.currentTime() - startTime) + "ms");   

    2. 大批量数据的执行进度:

     
    1.  LOG.debug("current progress: " + (currentPos * 100 / totalAmount) + "%");  

    3.关键变量及正在做哪些重要的事情:

    执行关键的逻辑,做IO操作等等

    1.  String getJVMPid() {  
    2.     String pid = "";  
    3.     // Obtains JVM process ID  
    4.     LOG.info("JVM pid is " + pid);  
    5.     return pid;  
    6.  }  
    7.    
    8.  void invokeRemoteMethod(Object params) {  
    9.      LOG.info("Calling remote method : " + params);  
    10.     //Calling remote server  
    11. }  

    应该避免怎样的日志方式?

    1. 混淆信息的Log

    日志应该是清晰准确的: 当看到日志的时候,你知道是因为连接池取不到连接导致的问题么?

    1.  Connection connection = ConnectionFactory.getConnection();  
    2.  if (connection == null) {  
    3.      LOG.warn("System initialized unsuccessfully");  
    4.  }  

    2. 记错位置

    产品代码中,使用console记录日志,导致没有找到日志。

         1. } catch (ConfigurationException e) {
         2.    e.printStackTrace();
         3. }

    3. 记错级别

    记错级别常常发生,常见的如:混淆代码错误和用户错误,如登录系统中,如果恶意登录,那系统内部会出现太多WARN,从而让管理员误以为是代码错误。可以反馈用户以错误,但是不要记录用户错误的行为,除非想达到控制的目的。

    1.  LOG.warn("Failed to login by "+username+");

    4. 遗漏信息

    这里可能包含两种情况:(1)用户自己少写了信息,导致毫无参考价值;(2)用户调用log的方式导致丢失信息,如下例,没有stack trace.

         1.  } catch (Exception ex) {
         2.   log.error(ex);
         3.  }

    总结:


    日志记录在程序员日常编程实践中必须面对的事情,本文针对这个话题谈了下自己的体会,希望读者能有所收益。多有不足,请多包涵。

    网友留言/评论

    我要留言/评论

    相关文章

    Lucene 基础理论与实例:Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。本文将介绍Lucene 基础理论与实例。
    List,set,Map 的用法和区别等:List按对象进入的顺序保存对象,不做排序或编辑操作。Set对每个对象只接受一次,并使用自己内部的排序方法(通常,你只关心某个元素是否属于Set,而不关心它的顺序--否则应该使用List)。Map同样对每个元素保存一份,但这是基于"键"的,Map也有内置的排序,因而不关心元素添加的顺序。如果添加元素的顺序对你很重要,应该使用 LinkedHashSet或者LinkedHashMap.
    web压力测试工具集介绍:当一套程序写完或者一台服务器配置完成后,相必很多朋友会像我一样,非 常想知道它到底能 够承受多大的负载压力,那在本文中,就给大家介绍十个免 费的可以用来进行 Web 的负载/压力测试的工具,这样,你就可以知道你的服务 器以及你的 Web 应用 能够顶得住多少的并发量,以及你的网站的性能。
    五种 JSP页面跳转方法详解:本文向您介绍Servlet页面跳转实现方法的几种区别,包括Servlet和JSP中的不同实现,比如Servlet中的redirect方式和forward方式得区别等。
    JAVA,HashSet面试题:本文列举java面试题中关于HashSet的一些知识点
    开源混淆工具ProGuard配置详解及配置实例:ProGuard是一个免费的java类文件压缩,优化,混淆器.它探测并删除没有使用的类,字段,方法和属性.它删除没有用的说明并使用字节码得到最大优化.它使用无意义的名字来重命名类,字段和方法.
    在代码重构中蜕变:这几天,要对我半年前写的代码进行一些整理工作,在看代码时发现当时有很多地方写得不够好,俗称的有“坏味道”,呵呵,重构,必须的。
    搞懂java中的synchronized关键字:实际上,我关于java的基础知识的90%以上都来自Thinking in Java。对于其中的synchronized关键字,当时就是浏览一下,大概知道意思,也没有细看。后来一直没有用过这个关键字。昨天看Thinking in Patterns with Java中的Observer模式,看了其中的Observable类的源码,发现里面几乎所有的方法都用了synchronized关键字(不是全部),其中个别用了synchronized(this){}的区块。于是,我发觉有必要好好理解一下这个关键字了。
    JAVA中synchronized与static synchronized 的区别:通过分析这两个关键字的分析,我们可以理解java中锁的概念。一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁)。实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。
    建造者模式(Builder)举例介绍:Builder模式的定义是:将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 首先它意图是要构建一个复杂的对像,而这个复杂的对像往往需要由好几个子对像或分步聚来完成最终的这个对象的构建,而这个复杂对象的子对像经常需要不断的变化,但它的构建过程是相对是稳定的。