Java异常整理及日志log学习

一、异常

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 可能会 捕获到其他异常。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一切唯有爱的真实 欧克 今天功课:时间是幻相?看来是继续破恐惧呀 太好玩了 感恩一切都恰恰好的安排,昨晚跟着链接把...
    赞跬步阅读 259评论 0 1
  • 昨天晚上,我正在吃饭,邻居大姐来了,她到我面前,神秘地问我说:“说是我们学校出事了?”我说:“出事?什么事?没...
    小风徐徐阅读 260评论 2 3
  • 光辉尽洒清幽梦, 多少离人仰望中。 千古情思常寄月, 嫦娥广袖卷愁空?
    繁花落尽深眸阅读 175评论 37 5
  • 起风了 落叶了 女孩该回家了 三年 她一个人安安静静 穿过陌生的帝都 走过熟悉的校园 明知道是场空欢喜 可还是义无...
    霍羌塘布姆阅读 365评论 0 1
  • 骤雨忽至 染就一池新绿 余音袅袅 似曾欲话我知 繁花散 胭脂染 孤烟风细细 无语燕喃呢 知音稀 情难寄 斜阳月又西...
    三更月阅读 237评论 2 15