孤尽31天之异常与日志处理

一、异常处理

java的异常处理分为很多异常,

1.1java异常体系

       java的异常处理流程和机制是比较完善的,如果能很好的处理异常机制,则是一个稳定软件和系统的重要表现。

1.1.1异常的重要的作用之一就是为系统安全护航

       优秀的异常处理应当遵守:

  1. 异常应当描述导致当前异常发生的原因
  2. 根据异常栈快速定位到异常发生的位置
  3. 结合异常描述和异常栈解决异常
  4. 异常返回的值应该与常规的值有明显区分
  5. 可以不用调用方来分析异常

1.2异常处理

 

       java的异常处理流程图

简单归纳就是首先由JVM生成异常对象,再判定是否有try捕获异常,在有try的情况下,判定是否所有异常都被捕获,如果未被捕获则再判定有没有finally语句?,如果没有则输出异常信息,如果有则执行finally代码,在执行finally时看异常是否处理,如果没有处理则也会输出异常。

异常处理机制:

java异常处理包括:生成异常、捕获异常、抛出异常、声明异常4部分。

如果抛出多种异常时如何捕获?

遵守从最小维度捕获异常到

方式一:如果多种异常都属于一个父类异常则只需要一个父类异常

方式二:如果有多种类型的异常则,需要将每个异常类型都进行捕获

异常规约:在调用二方包(即调用非本系统的方法)时,应该使用throwable(也就是error和exception的父类)来捕获,这样则能捕获所有异常。

1.但是如果捕获到了error这种不可抗力的异常则只能终止。

2.Exception时如果是

       受检与非受检是针对jvm来说,

       非受检异常RuntimeException(运行时异常)是不在人力控制之外的:

              如果是可预测的异常则需要提前检查

              如果是需捕获处理

              可透出异常:

       受检异常(checked):无能力,在可控范围之外

              引起注意:航班延误,我们无能为力

       坦然处理类:堵车,我们可以改变路线或者早出发

1.3异常抛出与捕获原则    

       包括:

  1. 非必要不使用异常

对于非稳定性代码进行捕获处理,稳定性代码则不需要,对于可以预测的可能异常则应该在运行之前进行预防处理,不需要进行捕获异常。

  1. 使用描述性消息抛出异常

向上抛出异常时,需要添加当前发生异常的相关信息,尽可能完整的封装后抛出

  1. 力所能及的异常一定要处理

能处理异常的时候应该处理,不要抛给上层

  1. 异常忽略要有理有据

              1.3.1java异常体系的try...catch...finally流程解析

其流程如下

这里的规约有

  1. 不要在finally里使用return,因为在finally中如果使用return会改变try中异常放入方法返回值中的变量值

但是在finally修改变量的值并不能改变try中已经放入方法返回的值。因为try中的return值会放入临时,所以在finally改变的值只是原来的t本身的值。但是方法要返回的是临时值而非原来的t值,但是如果finally里有return则返回finally的值而非原来准备返回的临时值。

              1.3.2 try-resource的流程

使用jdk7开始的try-resource时可能会导致的隐藏的资源未关闭

1.当try()中有多个资源,并且出现一个资源包含另外一个子资源时,如果发生异常时系统只会关闭父资源,不会自动关闭子资源不,因此需要将所有的资源都需要在外面进行申明这里需要注意。

资源可关闭的都会继承同一个接口closeable接口。

                                   2.当多个资源都抛出异常时,有的资源的关闭可能没有抛出时可能被最后一个吞没了,需要调用异常的support方法来循环获取原来的异常。

                                   3.在jdk9后可以在try之前申明资源,在try中包括资源对象即可。

              1.3.3 特殊NPE场景及其处理策略

                     级联调用时容易产生NPE(NullPointException),常规处理时就是首先进行null判断,有没优雅的办法解决多个NULL判定?

              可以使用Optional来使用如果为空使用orElse设置默认,可以使用ifPresent()来解决NPE问题,可以防止臃肿代码,这个跟lamda表达式。

              当使用null判定时如果在for循环中,当循环10000次时null判定与opption差不多都是30ms,但是如果超过10000次则option比null更加优秀。

              1.3.4 foreach遍历集合的异常

  1. 不要在foreach中进行元素的remove/add操作
  2. foreach循环会自动跳过遍历空集合(不管你在for循环体内编写什么代码都不会执行),如果对于有null值的集合,碰到null时需要注意NPE。

如果在foreach循环中抛出异常会有什么影响?

  1. 后续的操作都不会处理,造成极大浪费
  2. 应该尽可能的捕获异常不要抛出,而是在处理完成后抛出异常,

二 日志规约

       使用日志的作用主要包括:快速定位问题、记录行为轨迹、监控数据变化警告三个。

       快速定位问题:可根据日志快速分析出问题所在

其中记录行为轨迹:可以进行指标监控和链路追踪

       监控警告:可以进行健康检查和指标监控

       日志规约如下

              当天日志命名:应该是应用名.log保存

              过往日志命名:以{logname}.log.{保存日期}命名

              日志文件至少保存15天:为了追查以周为单位出现的问题

              敏感操作信息联机存储6个月:网络安全相关法律的规定,也可以作为与第三方分歧时使用,应该使用什么日志级别?应该使用warn级别。

       日志记录规约

              系统应依赖使用日志框架(SLF4J、JCL)的API而不是具体的日志库中的。JCL是apache推出的。Single Level for 4 java

      

因为如果你想改变日志框架时,可以通过日志适配器就能完成变更,所以尽可能使用它的框架层api,这样方便切换。

已经使用log4j的可以增加log4j-over-slf4j包来实现转到日志门面。

日志门面与具体的日志库的关系如下

从图中的可以得出,在应用系统调用log对象时,是loggerFactory_slf4j来调用staticLoggerBinder来绑定具体的日志库对象。

如果想更换logger框架时则需要使用适配器完成

   

该适配器通过

日志输出是尽量使用占位符

日志打印时禁止直接用json工具将对象转换为string

       只需要记录关键业务信息即可避免json工具转换时报错。

logback框架的核心配置

其中configuration 中的scan 为true则自动加载修改后的日志文件,不需要重启应用,并且设置scanPeriod=值为单位秒,

可以设置totalSizeCap来配置所有日志大小,配置了这个可以避免日志的增长占满硬盘。

直接将告警日志发送别的应用(钉钉,邮件等)

可以使用appender来实现,也可以发送给企业微信,但是这个需要自己实现。

logback日志框架关键类图

日志记录线程模型

可以得出可以使用同步和异步记录方式。

日志输出规约

  1. 日志级别开关判定

对于trace/debug/info级别的日志输出必须先进行日志级别判断,避

  1. 异常日志信息要完整

应该包括案发现场信息

扩展日志的设计规约

扩展日志需要单独存储

错误日志单独存储

       错误日志与正常的业务日志需要分开存储

1.4错误码规约

       错误码的功能包括系统与其他系统的沟通;人与系统的沟通;人与人之间的沟通等都需要很好的统一规范。

       错误码定义规约

              定义时字母数字组合使用

              要分级分类管理

              不要与业务架构或者使用者组织架构相关

              不能直接输出给用户作为提示信息使用

              使用者避免随意定义新的错误码

              便于开发者之间沟通

1.5异常与日志综合实践

       1.在controller层统一捕获异常

              通用业务调用逻辑如下

             

       日志的抛出处理应该在请求处理层处理,

       当各层分布式部署时,异常日志是否需要每层单独记录?

       是的,为了区分异常发生的具体层。

2.应该定义全局异常处理组件GlobalExceptionHandler的定义

       在全局异常组件时可以对各种异常进行分类处理,比如业务异常,系统异常等,对不同的类型异常可以分门别类的处理。例如对于系统日志可以通过发布日志事件,将日志信息发布到别的消息处理。

API层异常设计实践

      

  1. 严格约束条件判定

API要严格校验数据的合法性;基本约束判定;实体属性约束满足JSR303基础判定

客户端返回要有好:API层异常要给客户端返回状态码机器对应的错误消息

下层异常转译:

       service层异常设计实践

在DAO数据处理层异常、日志实践

使用MDC实现轻量级链路追踪

       将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如各个服务节点上的耗时,请求具体到那台机器,每个服务节点的

为什么要使用调用链路追踪?

使用有限的异常类处理业务中复杂多变的无限可能

使用一个通用的Exception和错误代码ErrorCode结合解决。

降低系统的维护难度和过度设计、冗余手段

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值