T31项目第5天

T31项目第5天


今天是孤尽班第三天学习,今天跟着宫宫老师学习了异常处理和日志,

异常处理和日志

1. java异常体系

异常应该描述当前异常发生的原因
根据异常栈快速定为异常的位置
结合异常描述和异常栈来解决异常

1.1异常处理流程

T31项目第5天
java获取异常之后,系统会判定是否有try,如果有try交给try处理,如果没有try处理交给jvm默认处理,如果try中有catch捕获,如果没有则交给jvm处理,有则交给catch来处理,catch处理完之后判断是否有 finally,有则交给finally处理,没有则交给jvm处理

1.2java异常处理机制

T31项目第5天
如果出现多个异常抓取异常处理方法
1.将方法的正确返回和异常返回分开处理,正确返回用return,异常返回用throw
2.使用多个catch,低层异常先抛出,在提升异常等级进行catch

1.4 java异常体系

error
java虚拟机的内存异常等,不可以人为处理
exception:
受检异常:可预测异常 和 需要 捕获异常(运行时的异常),受检异常异常和不受检异常(编译器编译时是否报错)

2. 异常处理

2.1异常抛出和捕获原则

1.非必要不使用异常
2.使用描述性异常抛出
3.力所能及的异常一定要处理
4.异常忽略要有理有据
总结:不要加太多异常,通过业务处理要减少异常抛出

2.2 java异常体系中 try …catch …finally

T31项目第5天
总结:
不要在try …catch .finally的代码块中增加return,会导致catch中处理结果失效,最后以return来返回结果集

2.3JDK中异常处理几种流程

T31项目第5天

总结:
1.如果在处理读取资源或者写出资源,开启链接等操作时,使用try…catch…finnaly处理,finnaly之后需要把资源或者链接关闭掉,避免引起jvm的io异常或者内存溢出异常

2.4特殊NPE处理

特殊NPE:连续性的属性调用,在代码中出现空指针,系统会抛出NULLPOINTException异常
**处理思路:**JDK1.8之后的optional的特性处理
optional.ofNullable(obj)
ofElse:当为空时,设置默认值
isPresent(Consumer <? super T> Consumer ) 在value没有值时做一些处理

2.5 特出异常情景及处理思路

foreach循环:再循环体中执行 add 和 remove 的操作,因为在遍历资源 会比对上一次修改次数和预期修改次数是否一致,如果不一致会抛出错误,foreach 中抛出异常之后后续不会执行,

3.日志规约

3.1日志功能

监控告警:健康检查,指标监控(告警)
记录行为轨迹:指标监控,链路(跟踪)
快速定位问题:

3.2日志时效规约

T31项目第5天
设置总结:日志命名方面:T31设置一个日志文件夹,文件夹名称未T31log,日志按照不同业务分为不同的文件夹,例如 订单业务日志文件夹名称未 order,按照年月日生成对应的文件夹的目录,每天的日志按照小时来生成一个独立的日志文件,文件名称为时分格式.log
**其他日志:**对于用户敏感信息和操作的日志要保存6个月以上,多机备份,用于一些业务后续处理

3.3日志记录规约

系统应该依赖日志框架(SLF4J,JCL)的API而不是日志库中
T31项目第5天
注意事项:

日志打印时不要用json工具将对象转换为string

logback 和SFL4J的关系:
logback是直接实现了slf4j的接口,是不消耗内存和计算开销的。而log4j不是对slf4j的原生实现,所以slf4j api在调用log4j时需要一个适配层。

3.3Logback的核心配置

T31项目第5天

3.4日志输出规约

日志级别(trace,debug,info)开关判断,
异常日志信息完整:案发现场信息和异常堆栈信息
避免重复日志打印:日志配置文件中配置additivity=false

3.5扩展日志规约

**扩展日志:**应用的 打点、临时监控、访问日志等
**存储方式:**业务日志和错误日志分开存储,扩展日志和其他日志分开存储

4.错误码规约

4.1错误码的功能

人与系统的沟通:通过错误码可以识别对应的系统错误
人与人之间沟通:统一错误处理的机制,保持错误处理思路的一致性
系统与系统之间的沟通:系统业务流转中,对于不同错误的信息的传递

4.1错误码的规约

T31项目第5天
**总结:**错误信息的定义需要遵循业务情况进行分类分析,对每种错误情况用错误码来表示

4.3错误码

T31项目第5天
**总结:**根据系统业务中的异常和正常请款,将日志不同情况设置不同的错误码,每种错误码对应不同的业务情况,做好描述

5.异常和日志实践

5.1 在controller层统一捕捉异常

T31项目第5天
**总结:**底层service.dao。manageger的异常在Controller层的统一处理

5.2 全局异常组件GlobalExceptionHandler来捕捉异常

package kr.weitao.starter.config;

import kr.weitao.common.exception.CommonException;
import kr.weitao.common.exception.ServiceException;
import kr.weitao.starter.model.DataResponse;
import kr.weitao.starter.model.Status;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.List;

/**
 * 全局统一异常处理
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandle {

    /**
     * 业务异常
     */
    @ExceptionHandler(value = ServiceException.class)
    public ResponseEntity<DataResponse> handle(ServiceException e) {
        e.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(e.getMsg());
        return ResponseEntity.ok().body(failure);
    }

    @ExceptionHandler(value = CommonException.class)
    public ResponseEntity<DataResponse> handle(CommonException e) {
        e.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(e.getMessage());
        return ResponseEntity.ok().body(failure);
    }

    /**
     * 400错误
     */
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public ResponseEntity<DataResponse> requestNotReadable(HttpMessageNotReadableException ex) {
        ex.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(failure);
    }

    /**
     * 400错误
     * @param ex
     * @return
     */
    @ExceptionHandler({TypeMismatchException.class})
    public ResponseEntity<DataResponse> requestTypeMismatch(TypeMismatchException ex) {
        ex.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(failure);
    }

    /**
     * 400错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public ResponseEntity<DataResponse> requestMissingServletRequest(MissingServletRequestParameterException ex) {
        ex.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(ex.getMessage());
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(failure);
    }

    /**
     * IO异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(IOException.class)
    public ResponseEntity<DataResponse> iOExceptionHandler(IOException ex) {
        ex.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(ex.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(failure);
    }

    /**
     * 405错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public ResponseEntity<DataResponse> request405(HttpRequestMethodNotSupportedException ex) {
        ex.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg("不支持请求方法");
        return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(failure);
    }


    /**
     * 超时异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(SocketTimeoutException.class)
    public ResponseEntity<DataResponse> SocketTimeoutException(SocketTimeoutException ex) {
        ex.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg("连接超时,请检查网络环境或重试");
        return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body(failure);
    }

    /**
     * 处理入参异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity<DataResponse> handleIllegalParamException(MethodArgumentNotValidException e) {
        e.printStackTrace();
        String message = "参数不合法";
        List<FieldError> errors = e.getBindingResult().getFieldErrors();
        if (errors.size() > 0) {
            message = errors.get(0).getDefaultMessage();
        }
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(message);
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(failure);
    }

    /**
     * 其他类型的异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = RuntimeException.class)
    public ResponseEntity<DataResponse> handle(Exception e) {
        e.printStackTrace();
        DataResponse failure = new DataResponse().setStatus(Status.FAILED).setMsg(e.getMessage());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(failure);
    }


}

5.3 API异常设计实实践T31项目第5天

**总结:**异常信息保证用户友好型,根据错误码来处理底层的错误信息向上抛出,

5.4 service 层的异常设计实践T31项目第5天

**总结:**避免脏数据的产生(null的处理),抛出对应异常码的信息,将相关的dao的错误信息向上层抛出

5.5 dao层异常

T31项目第5天
总结:使用daoException来封装异常向上抛出,选择性记录sql语句和操作时间信息

5.6MDC的追踪链路

T31项目第5天

T31项目第5天
实践:
Logback的MDC机制,日志模板中加入sessionId格式,在日志输出格式中指定输出sessionId

%d{yyyy-MM-dd HH:mm:ss.SSS}  [%X{sessionId}] -%5p ${PID:-} [%15.15t] %-40.40logger{39} : %m%n

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

*总结:
1.*使用通用serverException定义RuntimeException的通用ServiceException业务异常
2.结合与业务关联的错误码实现复杂多变的异常需求

上一篇:RestTemplate


下一篇:http请求util