关于异常处理:论一个优秀的工程师应该如何做好异常处理和日志记录

38次阅读

共计 2760 个字符,预计需要花费 7 分钟才能阅读完成。

异样解决

  • Java 类库中定义的能够通过 预查看 形式躲避的 RuntimeException 异样不应该通过 catch 形式来解决:

    • NullPointerException
    • IndexOutofBoundsException
    • 无奈通过预查看的异样除外: 在解析字符串模式数字时, 不得不通过 catch NumberFormatException 来实现

      if (obj != null) {}
  • 异样不要用来做流程管制, 条件管制:

    • 异样设计的初衷是解决程序运行中的各种意外状况, 且异样的解决效率比条件判断形式要低很多
  • 应用 catch 时要辨别稳固代码和非稳固代码:

    • 稳固代码: 无论如何不会出错的代码
    • 非稳固代码: 非稳固代码的 catch 尽可能辨别异样类型, 再做对应解决
    • 对于大段代码进行 try – catch, 会使得程序无奈依据不同的异样做出正确的应激反应, 也不利于定位问题

      • 在用户注册场景中, 如果用户输出非法字符, 或者用户名称已存在, 或者用户明码过于简略, 在程序上作出分门别类的判断, 并提醒给用户
  • 捕捉异样是为了解决, 不要捕捉了什么都不解决. 如果不须要解决, 应该将异样抛给调用者

    • 最外层的业务使用者, 必须解决异样, 将其转化为用户能够了解的内容
  • 如果有 try 块放到了事务代码中 ,catch异样后, 如果须要回滚事务, 肯定要留神 手动回滚事务
  • finally块必须对资源对象, 流对象进行敞开, 有异样也要做try – catch

    • JDK 7 当前, 能够应用try – with – resources 形式
  • 不要在 finally 块中应用return:

    • finally块中的 return 返回后办法完结执行, 不会再执行 try 块中的 return 语句
  • 捕捉异样与抛出异样必须齐全匹配, 或者是抛异样的父类
  • 办法的返回值能够为 null, 不强制返回空集合或者空对象等, 必须增加正文充分说明什么状况下会返回 null 值

    • 即便调用办法返回空集合或者空对象, 对于调用者来说, 必须思考到近程调用失败, 序列化失败, 运行时异样等返回 null 的场景
  • 肯定要防止出现 NPE 异样, 留神 NPE 产生的场景:

    • 返回类型为根本数据类型,return 包装数据类型的对象时, 主动拆箱有可能产生 NPE
    • 数据库的查问后果可能为 null
    • 汇合里的元素即便isNotEmpty, 取出的数据元素也可能为 null
    • 近程调用返回对象时, 一律要进行空指针判断, 避免 NPE
    • 对于 Session 中获取的数据, 倡议进行 NPE 查看, 防止空指针
    • 级联调用obj.getA().getB.getC(), 一连串的调用, 容易产生 NPE
    • JDK 8 应用 Optional 类来避免 NPE 问题
  • 定义时辨别 uncheckedchecked异样, 防止间接抛出 new RuntimeException(), 不容许抛出Exception 或者Throwable, 应该应用有业务含意的自定义异样

    • 举荐应用业务界已定义过的异样:

      • DAOException
      • ServiceException
  • 对于公司外的 http 或者 api 凋谢接口 必须应用 “ 错误码 ”; 利用外部 举荐 异样抛出; 跨利用间的 RPC 调用优先思考应用 Result 形式,封装 isSuccess()办法, 错误码, 谬误简短信息

    • RPC 办法应用 Result 形式的起因:

      • 应用抛异样返回形式, 调用方如果没有捕捉到就会产生运行时谬误
      • 如果不加栈信息, 只是 new 自定义异样, 退出本人了解的error message, 对于调用端解决问题的帮忙不会太多. 如果加了栈信息, 在频繁调用出错的状况下, 数据序列化和传输的性能损耗也是问题
  • 避免出现反复的代码, 即 DRY(Don’t Repeat Yourself)准则:

    • 反复的代码在当前的批改时, 须要批改所有的正本, 容易脱漏
    • 抽取共性办法, 或者形象公共类, 或者组件化

      • 一个类中有多个 public 办法, 都须要进行数行雷同的参数校验工作, 这个时候就要进行抽取:

        private boolean checkParam(DTO dto) {...}

        日志规约

  • 利用中不可间接应用日志零碎 (log4j,logback) 中的 API, 应该应用日志框架 slf4j 中的API, 应用门面模式的日志框架, 有利于保护和各个类的日志解决形式对立
  • 日志文件至多保留 15 天, 因为有些异样具备以 “ 周 ” 为频次产生的特点
  • 利用中的扩大日志 (打点, 长期监控, 拜访日志等) 命名形式:

    • appName_logType_logName.log

      • logType: 日志类型, 如 stats,monitor,access
      • logName: 日志形容
      • 这样通过文件名就能够晓得日志文件属于什么利用, 什么类型, 什么目标, 也不便归类查找

        • mppserver利用中独自监控时区转换异样: mppserver_monitor_timeZoneConvert.log
      • 对日志进行分类, 比方将谬误日志和业务日志离开寄存, 便于开发人员查看, 也便于对日志零碎进行及时监控
  • trace,debug,info级别的日志输入, 必须应用条件输入模式或者应用占位符形式

    logger.debug("Processing trade with id:" + id + "and symbol:" + symbol);
    • 如果日志级别是warn: 上述日志不会打印, 然而或执行字符串拼接操作
    • 如果 symbol 是对象, 会执行toString() 办法, 节约了系统资源, 执行上述操作, 最终日志却没有打印
    • 应用条件输入模式:

      if (logger.isDebugEnabled()) {logger.debug("Processing trade with id:" + id + "and symbol:" + symbol);
      }
    • 应用占位符输入模式:

      logger.debug("Processing trade with id: {} and symbol: {}, id, symbol);
  • 防止反复打印日志, 节约磁盘空间, 必须在 log4j.xml 中设置additivity=false

    <logger name="com.oxford.dubbo.config" additivity="false">
  • 异样信息包含:

    • 案发现场信息
    • 异样堆栈信息
    • 如果不解决, 应该通过异样关键字 throws 向上抛出

      logger.error(各类参数或者对象 toString() + "_" + e.getMessage(), e);
  • 审慎的记录日志:

    • 生产环境禁止输入 debug 日志
    • 有选择地输入 info 日志
    • 如果应用 warn 来记录刚上线时的业务行为信息, 肯定要留神日志输出量问题, 防止服务器内容过多, 并及时删除这些察看日志

      • 大量地输入有效日志, 不利于零碎性能的晋升, 也不利于疾速定位谬误点
      • 记录日志时须要思考:

        • 这些日志真的有人看吗?
        • 看到这条日志可能做什么?
        • 能不能给排查问题带来益处?
  • 能够应用 warn 日志级别来记录用户输出参数谬误的状况
  • 留神日志的输入级别:

    • error 级别只记录零碎逻辑出错, 异样或者重要的错误信息
  • 应用全英文来正文和形容日志错误信息

正文完
 0