异常日志收集

使用spring的aop来实现项目异常日志收集入库功能

相关注解

  • @Aspect:用于定义切面
  • @Before:通知方法会在目标方法调用之前执行
  • @After:通知方法会在目标方法返回或抛出异常后执行
  • @AfterReturning:通知方法会在目标方法返回后执行
  • @AfterThrowing:通知方法会在目标方法抛出异常后执行
  • @Around:通知方法会将目标方法封装起来
  • @Pointcut:定义切点表达式

切点表达式

  • 细粒度到方法

execution(方法修饰符 返回类型 方法所属的包.类名.方法名称(方法参数)

//org.jeecg.modules包及其子包下所有类中的public方法都应用切面里的通知
@Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..))")
  • 细粒度到指定注解
//带@AutoLog注解的方法都应用切面里的通知
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLog)")

注:前者应用广不需要在方法上声明就可以覆盖到,后者需要在每个方法都声明注解,可以在注解类配置个性化的参数。实际开发中需要根据不同业务场景使用两者

系统异常日志类

public class ExceptionLogDTO {
 
    private Integer id;
 
    /**请求方法*/
    private String method;
 
    /**请求说明*/
    private Integer content;
 
    /**异常信息*/
    private String exMessage;
 
    /**异常名称*/
    private String exName;
 
    /**错误类型*/
    private Integer exType;
 
    /**IP地址*/
    private String ip;
 
    /**参数*/
    private String param;
 
    /**是否删除*/
    private Integer isDeleted;
 
    /**创建人*/
    private String createUserId;
 
    /**创建时间*/
    private Date createTime;
}

/**
   * 异常收集
   * @param joinPoint
   * @param e
   */
  @AfterThrowing(value = "logPointCut()",throwing = "e")
  public void afterThrowable(JoinPoint joinPoint, Exception e) throws Throwable {
      ExceptionLogDTO dto = new ExceptionLogDTO();
      String message = stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace());
      dto.setExMessage(message);
      dto.setExName(e.getMessage());
      MethodSignature signature = (MethodSignature) joinPoint.getSignature();
      Method method = signature.getMethod();
      AutoLog syslog = method.getAnnotation(AutoLog.class);
      //请求的方法名
      String className = joinPoint.getTarget().getClass().getName();
      String methodName = signature.getName();
      dto.setMethod(className + "." + methodName + "()");
      //设置请求说明
      dto.setContent(getOperateType(methodName, syslog.operateType()));
      //获取request
      HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
      //请求的参数
      dto.setParam(getReqestParams(request,joinPoint));
      //设置IP地址
      dto.setIp(IPUtils.getIpAddr(request));
      //获取登录用户信息
      LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
      if(sysUser!=null){
          dto.setCreateUserId(sysUser.getUsername());
      }
      //保存系统日志
      baseCommonService.addExceptionLog(dto);
  }
   
  /**
   * 转换异常信息为字符串
   *
   * @param exceptionName    异常名称
   * @param exceptionMessage 异常信息
   * @param elements         堆栈信息
   */
  public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
      StringBuffer strbuff = new StringBuffer();
      for (StackTraceElement stet : elements) {
          strbuff.append(stet + "\n");
      }
      String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
      return message;
  }
   
  /**
   * @Description: 获取请求参数
   * @param request:  request
   * @param joinPoint:  joinPoint
   * @Return: java.lang.String
   */
  private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
      String httpMethod = request.getMethod();
      String params = "";
      if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
          Object[] paramsArray = joinPoint.getArgs();
          // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
          //  https://my.oschina.net/mengzhang6/blog/2395893
          Object[] arguments  = new Object[paramsArray.length];
          for (int i = 0; i < paramsArray.length; i++) {
              if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
                  //ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                  //ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
                  continue;
              }
              arguments[i] = paramsArray[i];
          }
         
          PropertyFilter profilter = new PropertyFilter() {
              @Override
              public boolean apply(Object o, String name, Object value) {
                  if(value!=null && value.toString().length()>500){
                      return false;
                  }
                  return true;
              }
          };
          params = JSONObject.toJSONString(arguments, profilter);
       
      } else {
          MethodSignature signature = (MethodSignature) joinPoint.getSignature();
          Method method = signature.getMethod();
          // 请求的方法参数值
          Object[] args = joinPoint.getArgs();
          // 请求的方法参数名称
          LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
          String[] paramNames = u.getParameterNames(method);
          if (args != null && paramNames != null) {
              for (int i = 0; i < args.length; i++) {
                  params += "  " + paramNames[i] + ": " + args[i];
              }
          }
      }
      return params;
  }

接口测试

  • 接口返回报错信息
    在这里插入图片描述
  • 数据库正确记录相关异常信息
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值