> 文章列表 > Springboot异常统一处理,并保存异常日志到数据库中

Springboot异常统一处理,并保存异常日志到数据库中

Springboot异常统一处理,并保存异常日志到数据库中

一、为什么要进行统一异常处理

  1. 如果发生了异常我们应该让接口可以返回统一的结果。有好的展示给接口调用方。
  2. 方便我们对异常进行记录,和错误排查。
  3. 我们可能对某些异常比较关注,比如说我们监控某个IP或者用户一天发送短信的数量,当超出一定数量后我们就不再发送然后抛出异常。这个时候我们通过统一异常处理进行全局拦截,然后记录日志甚至是数据库,并且发送短信通知相关负责人。

源码:https://gitee.com/charlinchenlin/store-pos

二、统一异常处理概述

1、统一异常处理介绍

Spring在3.2版本增加了一个注解@ControllerAdvice,可以与@ExceptionHandler、@InitBinder、@ModelAttribute 等注解注解配套使用。不过跟异常处理相关的只有注解@ExceptionHandler,从字面上看,就是异常处理器 的意思

2、原理和目标

简单的说,该注解可以把异常处理器应用到所有控制器,而不是单个控制器。借助该注解,我们可以实现:在独立的某个地方,比如单独一个类,定义一套对各种异常的处理机制,然后在类的签名加上注解@ControllerAdvice,统一对 不同阶段的、不同异常 进行处理。这就是统一异常处理的原理。

对异常按阶段进行分类,大体可以分成:进入Controller前的异常 和 Service 层异常
Springboot异常统一处理,并保存异常日志到数据库中

三、项目中的异常

1、制造异常

屏蔽IntegralGrade实体类 中的 @TableField注解

@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
//@TableField("is_deleted")
@TableLogic
private Boolean deleted;

2、Swagger中测试

测试列表查询功能,查看结果,发生错误,显示响应失败,因为deleted在数据库找不到对应的列
Springboot异常统一处理,并保存异常日志到数据库中

四、实现统一异常处理

目标:我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要进行统一异常处理。

1、创建自定义业务异常类

public class BusinessException extends RuntimeException {private static final long serialVersionUID = 1L;private int code;private String msg;public BusinessException(int code) {this.code = code;this.msg = MessageUtils.getMessage(code);}public BusinessException(int code, String... params) {this.code = code;this.msg = MessageUtils.getMessage(code, params);}public BusinessException(int code, Throwable e) {super(e);this.code = code;this.msg = MessageUtils.getMessage(code);}public BusinessException(int code, Throwable e, String... params) {super(e);this.code = code;this.msg = MessageUtils.getMessage(code, params);}public BusinessException(String msg) {super(msg);this.code = ErrorCode.INTERNAL_SERVER_ERROR;this.msg = msg;}public BusinessException(String msg, Throwable e) {super(msg, e);this.code = ErrorCode.INTERNAL_SERVER_ERROR;this.msg = msg;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}}

异常工具类

public class ExceptionUtils {/* 获取异常信息* @param ex  异常* @return    返回异常信息*/public static String getErrorStackTrace(Exception ex){StringWriter sw = null;PrintWriter pw = null;try {sw = new StringWriter();pw = new PrintWriter(sw, true);ex.printStackTrace(pw);}finally {try {if(pw != null) {pw.close();}} catch (Exception e) {}try {if(sw != null) {sw.close();}} catch (IOException e) {}}return sw.toString();}
}

异常代码类

public interface ErrorCode {int INTERNAL_SERVER_ERROR = 500;int UNAUTHORIZED = 401;int NOT_NULL = 10001;......
}

2、创建统一返回结果类

@ApiModel(value = "响应")
@Data
public class Result<T> implements Serializable {private static final long serialVersionUID = 1L;/* 编码:0表示成功,其他值表示失败*/@ApiModelProperty(value = "编码:0表示成功,其他值表示失败")private int code = 0;/* 消息内容*/@ApiModelProperty(value = "消息内容")private String msg = "success";/* 响应数据*/@ApiModelProperty(value = "响应数据")private T data;public Result<T> ok(T data) {this.setData(data);return this;}public boolean success(){return code == 0;}public Result<T> error() {this.code = ErrorCode.INTERNAL_SERVER_ERROR;this.msg = MessageUtils.getMessage(this.code);return this;}public Result<T> error(int code) {this.code = code;this.msg = MessageUtils.getMessage(this.code);return this;}public Result<T> error(int code, String msg) {this.code = code;this.msg = msg;return this;}public Result<T> error(String msg) {this.code = ErrorCode.INTERNAL_SERVER_ERROR;this.msg = msg;return this;}
}

3、创建异常日志表、实体类及实现

实体类

@Data
@EqualsAndHashCode(callSuper=false)
@TableName("sys_log_error")
public class SysLogErrorEntity extends BaseEntity {private static final long serialVersionUID = 1L;/* 请求URI*/private String requestUri;/* 请求方式*/private String requestMethod;/* 请求参数*/private String requestParams;/* 用户代理*/private String userAgent;/* 操作IP*/private String ip;/* 异常信息*/private String errorInfo;}

实现

@Service
public class SysLogErrorServiceImpl extends BaseServiceImpl<SysLogErrorDao, SysLogErrorEntity> implements SysLogErrorService {@Overridepublic PageData<SysLogErrorDTO> page(Map<String, Object> params) {IPage<SysLogErrorEntity> page = baseDao.selectPage(getPage(params, Constant.CREATE_DATE, false),getWrapper(params));return getPageData(page, SysLogErrorDTO.class);}@Overridepublic List<SysLogErrorDTO> list(Map<String, Object> params) {List<SysLogErrorEntity> entityList = baseDao.selectList(getWrapper(params));return ConvertUtils.sourceToTarget(entityList, SysLogErrorDTO.class);}private QueryWrapper<SysLogErrorEntity> getWrapper(Map<String, Object> params){QueryWrapper<SysLogErrorEntity> wrapper = new QueryWrapper<>();return wrapper;}@Override@Transactional(rollbackFor = Exception.class)public void save(SysLogErrorEntity entity) {insert(entity);}}

4、创建统一异常处理器,并保存系统异常信息

@RestControllerAdvice
@Slf4j
public class AdminExceptionHandler {@Autowiredprivate SysLogErrorService sysLogErrorService;/* 处理自定义异常*/@ExceptionHandler(BusinessException.class)public Result handleRenException(BusinessException ex){log.error(ex.getMessage(), ex);Result result = new Result();result.error(ex.getCode(), ex.getMsg());return result;}@ExceptionHandler(DuplicateKeyException.class)public Result handleDuplicateKeyException(DuplicateKeyException ex){Result result = new Result();result.error(ErrorCode.DB_RECORD_EXISTS);return result;}@ExceptionHandler(Exception.class)public Result handleException(Exception ex){log.error(ex.getMessage(), ex);saveLog(ex);return new Result().error();}/* 保存异常日志*/private void saveLog(Exception ex){SysLogErrorEntity errorEntity = new SysLogErrorEntity();//请求相关信息HttpServletRequest request = HttpContextUtils.getHttpServletRequest();errorEntity.setIp(IpUtils.getIpAddr(request));errorEntity.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));errorEntity.setRequestUri(request.getRequestURI());errorEntity.setRequestMethod(request.getMethod());Map<String, String> params = HttpContextUtils.getParameterMap(request);if(MapUtil.isNotEmpty(params)){errorEntity.setRequestParams(JsonUtils.toJsonString(params));}//异常信息errorEntity.setErrorInfo(ExceptionUtils.getErrorStackTrace(ex));//保存sysLogErrorService.save(errorEntity);}
}