T31项目第5天
- 异常处理和日志
- 1. java异常体系
- 2. 异常处理
- 3.日志规约
- 4.错误码规约
- 5.异常和日志实践
- 5.1 在controller层统一捕捉异常
- 5.2 全局异常组件GlobalExceptionHandler来捕捉异常
- 5.3 API异常设计实实践![在这里插入图片描述](https://www.icode9.com/i/ll/?i=02efbb4226e74ed09b717dbb3aaf051c.png?,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6aOe5oms5pm06Zuq,size_20,color_FFFFFF,t_70,g_se,x_16)
- 5.4 service 层的异常设计实践![在这里插入图片描述](https://www.icode9.com/i/ll/?i=e5d2efe73e63489596516694b97ae60b.png?,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6aOe5oms5pm06Zuq,size_20,color_FFFFFF,t_70,g_se,x_16)
- 5.5 dao层异常
- 5.6MDC的追踪链路
- 5.7 用有限的异常处理业务中复杂多变可能
今天是孤尽班第三天学习,今天跟着宫宫老师学习了异常处理和日志,
异常处理和日志
1. java异常体系
异常应该描述当前异常发生的原因
根据异常栈快速定为异常的位置
结合异常描述和异常栈来解决异常
1.1异常处理流程
java获取异常之后,系统会判定是否有try,如果有try交给try处理,如果没有try处理交给jvm默认处理,如果try中有catch捕获,如果没有则交给jvm处理,有则交给catch来处理,catch处理完之后判断是否有 finally,有则交给finally处理,没有则交给jvm处理
1.2java异常处理机制
如果出现多个异常抓取异常处理方法
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
总结:
不要在try …catch .finally的代码块中增加return,会导致catch中处理结果失效,最后以return来返回结果集
2.3JDK中异常处理几种流程
总结:
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设置一个日志文件夹,文件夹名称未T31log,日志按照不同业务分为不同的文件夹,例如 订单业务日志文件夹名称未 order,按照年月日生成对应的文件夹的目录,每天的日志按照小时来生成一个独立的日志文件,文件名称为时分格式.log
**其他日志:**对于用户敏感信息和操作的日志要保存6个月以上,多机备份,用于一些业务后续处理
3.3日志记录规约
系统应该依赖日志框架(SLF4J,JCL)的API而不是日志库中
注意事项:
日志打印时不要用json工具将对象转换为string
logback 和SFL4J的关系:
logback是直接实现了slf4j的接口,是不消耗内存和计算开销的。而log4j不是对slf4j的原生实现,所以slf4j api在调用log4j时需要一个适配层。
3.3Logback的核心配置
3.4日志输出规约
日志级别(trace,debug,info)开关判断,
异常日志信息完整:案发现场信息和异常堆栈信息
避免重复日志打印:日志配置文件中配置additivity=false
3.5扩展日志规约
**扩展日志:**应用的 打点、临时监控、访问日志等
**存储方式:**业务日志和错误日志分开存储,扩展日志和其他日志分开存储
4.错误码规约
4.1错误码的功能
人与系统的沟通:通过错误码可以识别对应的系统错误
人与人之间沟通:统一错误处理的机制,保持错误处理思路的一致性
系统与系统之间的沟通:系统业务流转中,对于不同错误的信息的传递
4.1错误码的规约
**总结:**错误信息的定义需要遵循业务情况进行分类分析,对每种错误情况用错误码来表示
4.3错误码
**总结:**根据系统业务中的异常和正常请款,将日志不同情况设置不同的错误码,每种错误码对应不同的业务情况,做好描述
5.异常和日志实践
5.1 在controller层统一捕捉异常
**总结:**底层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异常设计实实践
**总结:**异常信息保证用户友好型,根据错误码来处理底层的错误信息向上抛出,
5.4 service 层的异常设计实践
**总结:**避免脏数据的产生(null的处理),抛出对应异常码的信息,将相关的dao的错误信息向上层抛出
5.5 dao层异常
总结:使用daoException来封装异常向上抛出,选择性记录sql语句和操作时间信息
5.6MDC的追踪链路
实践:
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.结合与业务关联的错误码实现复杂多变的异常需求