美文网首页
Java异常整理及日志log学习

Java异常整理及日志log学习

作者: Loon1993 | 来源:发表于2018-11-22 14:47 被阅读0次

    一、异常

    www:what why how

    • What

      1. 定义:《Java编程思想》:异常情形(Exception condition)是指阻止当前方法或作用域继续执行的问题。把异常情形与普通问题区分很重要,普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误。异常情形,就不能继续下去,因为当前环境下无法获得必要的信息来解决问题。需要从当前环境退出,并且把问题抛给上一级环境。

      例如:当进行除法运算,被除数等于0的情况,作为一种非法输入,需要抛出异常。

      public class Calculator {
          public int devide(int num1, int num2) {
              if(num2 == 0) {
                  throw new IllegalArgumentException("除数不能为零");
              }
              return num1/num2;
          }
      }
      
      1. 异常的基本体系:
      image
      • Throwable ,作为所有异常与错误的超类,类似定义一切都可抛的东西,其中具有两个子类,ErrorException
      • Error,用于指示合理的应用程序不应该试图捕获的严重问题。遇到Error,说明出现的错误以及不是代码层面的问题了,建议谷歌。
      • Exception,它指出了合理的应用程序想要捕获的条件。Exception又分为两类:一种是CheckedException,一种是UncheckedException。这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常UncheckedException又叫做RuntimeException。《effective java》指出:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。
    • Why

      • 异常处理机制作用

        1. 告警

        2. 方便问题的排查

        3. 避免出现的异常问题影响下面的代码运行

        4. 针对出现的异常情况,进行捕获,以及可以做相应的恢复处理

        5. 自定义异常,符合面向对象思想,处理业务异常

      • 自定义异常

        public class NormandyCheckedException extends Exception {
            //异常传递cause
            public NormandyCheckedException(String message, Throwable cause) {
                super(message, cause);
            }
        }
        
        1. 表示与业务相关的问题
        2. 告知调用方需要在开发过程中考虑部分的逻辑
        3. 需要记录异常发生的栈信息以及message
    • How

      1. 异常的通常处理手段

        • 对代码块用try..catch进行异常捕获处理;

        • 在 该代码的方法体外用throws进行抛出声明,你需要谨慎处理。下游方在调用你的方法时,需要针对你抛出的异常进行处理。

          此时有两种情况:

          • Exception及子类 必须手动 catch 或者 throws

          • RuntimeExcption及子类 可以选择忽略

        • 在代码块用throw手动抛出一个异常对象,此时也有两种情况,跟2)中的类似:

          • 如果抛出的异常对象是Exception,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。
          • 如果抛出的异常对象是运行时异常,此方法的调用者可以选择地进行异常捕获处理。
          • 如果最终将异常抛给main方法,则相当于交给jvm自动处理,jvm会简单地打印异常信息
    1. Try catch

      • 先catch子类Exception 若父类Exception在子类之前会覆盖

      • 尽量进行小范围的try catch

      • 尽量避免用Exception捕获异常,其会捕获受检异常与非受检异常


      • catch异常之后需要打印出堆栈信息(切记不能重复打印)

      • 不能存在空catch块

      • 不要捕获Error(RuntimeException)

      • 尽量在高层进行异常的捕获操作

      • 避免异常丢失(见代码)

    2. throw/throws

      throw:在方法内部主动抛出异常

      throws:当方法中存在未catch的受检异常,需要在方法签名后面抛出

      • 当捕获到异常之后,希望在下一级调用中处理,可以通过throw
      • 异常传递:传递 cause 或者调用initCause方法
      • catch块中throw出Exception,就不需要进行log日志记录,避免重复记录了异常记录
      • 异常限制:在类继承的时候,方法覆盖时进行异常抛出声明,子类可以不抛出异常或则只能抛出父类的异常或其子类异常
      • 尽量不要用异常作为控制流程的方式,带来额外的消耗
    3. finally

      • 跟随在try 之后,在方法返回之前必然执行finally
      • 一般在finally中进行资源的close操作
    4. try with resource(一定需要了解资源的close方法内部的实现逻辑。否则还是可能会导致资源泄露。)

      public class Demo {
          public static void main(String[] args) {
              BufferedInputStream bin = null;
              BufferedOutputStream bout = null;
              try {
                  bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
                  bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")));
                  int b;
                  while ((b = bin.read()) != -1) {
                      bout.write(b);
                  }
              }
              catch (IOException e) {
                  e.printStackTrace();
              }
              finally {
                  if (bin != null) {
                      try {
                          bin.close();
                      }
                      catch (IOException e) {
                          e.printStackTrace();
                      }
                      finally {
                          if (bout != null) {
                              try {
                                  bout.close();
                              }
                              catch (IOException e) {
                                  e.printStackTrace();
                              }
                          }
                      }
                  }
              }
          }
      }
      
      image
    public class TryWithResource {
        public static void main(String[] args) {
            try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
                 BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
                int b;
                while ((b = bin.read()) != -1) {
                    bout.write(b);
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 总 结

    1. 使用异常一般是内部系统调用,包括rpc调用方式。但是针对外部调用时,可以使用约定的code表示,而不应直接抛异常。
    2. 针对可恢复的情况使用受检异常,针对编程错误使用runtime exception
    3. 优先使用java提供的标准异常
    4. 抛出异常的方法,可以加上javaDoc 的@throw标记说明
    5. 当产生异常情况下,尽量使整体方法执行保持原子性
    6. 不要忽略异常:不要使用空catch块

    思考

    到处都是try catch 是好是坏?

    二、日志

    www:what why how

    • What

      日志文件是用于记录系统操作事件的记录文件或文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。

    • Why

      1. 便于定位问题
      2. 观察分支是否执行
      3. 观察主流程是否成功运行
    • How

      1. 日志的级别

        • Trace:特别详细的系统运行完成信息,业务代码中,建议不要使用
        • Debug:针对开发测试环节中,打印需要测试的流程信息(线上一般需要关闭debug级别的日志)
        • Info: 系正常运行信息
        • **Warn: **不应该出现但是不影响程序、当前请求正常运行的异常情况
        • **Error: **影响到程序正常运行、当前请求正常运行的异常情况
      2. 打日志的方式

        • 必须使用参数化信息的方式

        • 如有参数变量,使用[]隔离。分隔符用统一分隔符,便于统计(详情,@青华) "[]"的正则问题

          logger.debug("Processing trade_with_id_:[{}] and symbol : [{}] ", id, symbol);
          
        • 不要进行字符串拼接,那样会产生很多String对象,占用空间,影响性能。
          反例:

          logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
          
        • 尽量打印日志的上下文(如if 分支),方便定位运行的流程 [远程调试方法]

        • 遵循TraceId的原则,可以根据某个id在日志中体现出整体运行流程

        • 如果日志中需要打印对象信息,尽量重新toString() 方法 或着 只打印对象的部分字段(id、no等关键信息)

          log.info("user credit material uid:{}, materialInfo:{}", uid, JSON.toJSONString(materialApplyInfo));
          
        • 遇到异常,在catch中需要记录,但避免重复记录

          try{
              doService(int id);
          }catch(Exception e) {
              log.error("Service Exception...,id:[{}]", id, ex);
              throw e;
          }
          
        • 避免出现用户的隐私内容,用uid等代替用户信息

        • 调用外部接口,打印出来入参与出参

    Code Review

    1. 使用exception,保持处理流程的原子性。

    2. 通过log.error记录日志,并且进行数据统计。

    3. 在对外输出的时候,只是通过OpenResponse进行反馈,实现封装性。

    4. 在log 日志中,没有记录对应aid 或者能代表出问题的订单。算是无效的日志。

    5. 在遇到某些业务异常,可以直接return,但是通过 catch Exception 可能会 捕获到其他异常。

    相关文章

      网友评论

          本文标题:Java异常整理及日志log学习

          本文链接:https://www.haomeiwen.com/subject/gaelqqtx.html