一个在生产环境里运行的程序若是没有日志是很让维护者提心吊胆的,有太多杂乱又无心义的日志也是使人伤神。程序出现问题时候,从日志里若是发现不了问题可能的缘由是很使人受挫的。本文想讨论的是如何在Java程序里写好日志。html
通常来讲日志分为两种:业务日志和异常日志,使用日志咱们但愿能达到如下目标:java
Java的日志框架太多了。。。apache
选项太多了的后果就是选择困难症,个人见解是没有最好的,只有最合适的。在比较关注性能的地方,选择Logback或本身实现高性能Logging API可能更合适;在已经使用了Log4j的项目中,若是没有发现问题,继续使用多是更合适的方式;我通常会在项目里选择使用Slf4j, 若是不想有依赖则使用java.util.logging或框架容器已经提供的日志接口。api
日志变量每每不变,最好定义成final static,变量名用大写。缓存
Java的日志框架通常会提供如下日志级别,缺省打开info级别,也就是debug,trace级别的日志在生产环境不会输出,在开发和测试环境能够经过不一样的日志配置文件打开debug级别。服务器
在程序里要合理使用日志分级: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;
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:参考