论坛首页新手上路 签到
查看: 5402|回复: 0

[知识分享] Springmvc中切面打印请求响应日志,捕获异常

[复制链接]
金智-周雷

该用户从未签到

11

主题

11

帖子

33

积分

初来乍到

凡人

Rank: 1

积分
33
发表于 2017-9-2 13:54:48 | 显示全部楼层 |阅读模式
本帖最后由 leizhou 于 2017-9-2 18:59 编辑

Springmvc中切面打印请求响应日志,捕获异常

------------------------------------------------------
目的:
        通常不允许直接将异常堆栈信息显示在前端,而必须经过转义为通用性异常描述,以示友好,且避免安全漏洞问题。
        利用Springmvc中切面,打印请求响应日志,捕获异常并转义为通用性异常描述。

------------------------------------------------------
1)常见做法
注:只能拦截controller请求,无法拦截soap请求。注:仅捕获异常并转义为通用性异常描述。
  1. @ControllerAdvice
  2. public class GlobalControllerAdvice {

  3.         private LoggerDecorate LOGGER = LoggerDecorateFactory.getLogger(this.getClass());

  4.     /**
  5.      * 异常控制
  6.      *
  7.      */
  8.     @ExceptionHandler(Exception.class)
  9.     public ModelAndView defaultErrorHandler(HttpServletResponse resp, Exception e) throws Exception {
  10.         LOGGER.error("执行异常:", e);
  11.         String logId = MDC.get("logId");// 这里建议应该输出logId,以便到日志文件中定义异常
  12.         if (HttpUtil.isAjaxRequest()) {
  13.             Map<String, Object> model = new HashMap<>();
  14.             model.put("logId", logId);
  15.             model.put("msg", e instanceof MyException ? e.getMessage() : "系统异常");
  16.             return new ModelAndView(new MappingJackson2JsonView(), model);
  17.         }
  18.         ModelAndView mav = new ModelAndView();
  19.         mav.addObject("logId", logId);
  20.         mav.addObject("msg", e instanceof MyException ? e.getMessage() : "系统异常");
  21.         //mav.addObject("err", e);// 若输出异常堆栈信息,则页面显示不友好,且可能暴露安全漏洞
  22.         mav.setViewName("error");// 自定义error页面,显示logId和msg
  23.         return mav;
  24.     }
复制代码


  1.     /**
  2.      * 判断是否ajax请求
  3.      */
  4.     public static boolean isAjaxRequest() throws Exception {
  5.         return "XMLHttpRequest".equals(getRequest().getHeader("X-Requested-With"));
  6.     }
复制代码


------------------------------------------------------
1)统一做法

注:利用自定义切面,拦截controller请求,和拦截webservice请求。
注:接口返回类型只支持JavaBean对象、Integer和String,不能为void.若是某些特殊类型,如Map,也需修改下面的代码进行非常简单的特殊处理。
注:使用springmvc自带的校验参数的注解@Valid,不支持webservice接口。建议自定义注解进行参数校验,见下面代码中的validBaseReq(arguments);

  1. /**
  2. * <pre>
  3. * 描述:切面记录请求日志,拦截各种接口。
  4. * </pre>
  5. * @author 01116253
  6. * @since 1.0
  7. */
  8. @Aspect
  9. @Component
  10. public class LogAspect {
  11.     private static LoggerDecorate logger = LoggerDecorateFactory.getLogger(LogAspect.class);
  12.     private static final String REQ = "REQ";
  13.     private static final String RSP = "RSP";

  14.     @Pointcut("execution(* com.wisedu.api.message.controller..*Controller.*(..))"
  15.             + "|| execution(* com.wisedu.api.app.controller..*Controller.*(..))"
  16.             + "|| execution(* com.wisedu.api.transfer.controller..*Controller.*(..))")
  17.     public void api() {
  18.     }

  19.     @Around("api()")
  20.     public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
  21.         long begin = System.currentTimeMillis();

  22.         Class<?> clazz = pjp.getSignature().getDeclaringType();
  23.         String className = clazz.getName();
  24.         String methodName = pjp.getSignature().getName();
  25.         Object[] arguments = pjp.getArgs();

  26.         try {
  27.             // 打印请求日志
  28.             logger.info(LogUtil.getMsg(REQ, className, methodName, arguments));
  29.         } catch (MyException e) {
  30.             logger.error("切面打印请求失败", e);
  31.         } catch (Exception e) {
  32.             logger.error("切面打印请求失败", e);
  33.         }
  34.         try {
  35.             validBaseReq(arguments);
  36.             // GlobalExceptionHandler统一异常处理类,无法拦截soap的顶层异常,因此在这里处理
  37.             Object rsp = pjp.proceed();
  38.             logger.info(className + " | " + methodName + " cost time:{}", (System.currentTimeMillis() - begin));
  39.             return rsp;
  40.         } catch (MyException e) {
  41.             return getErrorResult(e.getStatus(), e.getMsg(), begin, clazz, className, methodName, arguments, e);
  42.         } catch (Exception e) {
  43.             return getErrorResult(ResultCode.OTHER_ERROR, "内部异常", begin, clazz, className, methodName, arguments, e);
  44.         }

  45.     }

  46.     // 当接口调用失败后,返回一个异常对象
  47.     private Object getErrorResult(int code, String msg, long begin, Class<?> clazz, String className, String methodName,
  48.             Object[] arguments, Throwable t) {
  49.         Object obj = null;
  50.         Method[] allMethods = clazz.getDeclaredMethods();
  51.         Method method = null;
  52.         for (Method tmpMethod : allMethods) {
  53.             if (methodName.equals(tmpMethod.getName())) {
  54.                 method = tmpMethod;
  55.                 break;
  56.             }
  57.         }
  58.         if (null == method) {
  59.             logger.info("the method [{}] has no return type", new Object[] { methodName });
  60.             obj = new BaseResult();
  61.         } else {
  62.             Class<?> rt = method.getReturnType();
  63.             if (null == rt) {
  64.                 logger.info("the method [{}] has no return type", new Object[] { methodName });
  65.                 obj = new BaseResult();
  66.             } else {
  67.                 String rtName = StringUtils.trimToEmpty(rt.getName());
  68.                 if (rtName.endsWith("Integer")) {
  69.                     obj = Integer.valueOf(0);
  70.                 } else if (rtName.endsWith("String")) {
  71.                     obj = "error";
  72.                 } else {// TODO 返回类型的特殊处理
  73.                     try {
  74.                         obj = rt.newInstance();
  75.                     } catch (Exception e) {
  76.                         logger.error("the method [{}] return type newInstance err", new Object[] { methodName, e });
  77.                         obj = new BaseResult();
  78.                     }
  79.                 }
  80.             }
  81.         }
  82.         if (((obj instanceof Integer)) || ((obj instanceof String))) {
  83.             long space = System.currentTimeMillis() - begin;
  84.             logger.error(LogUtil.getMsg(new Object[] { RSP, className, methodName, obj, Long.valueOf(space) }),
  85.                     new Object[] { t });
  86.             return obj;
  87.         }
  88.         BaseResult rsp = (BaseResult) obj;
  89.         rsp.setCode(code);
  90.         rsp.setMsg(msg);

  91.         long space = System.currentTimeMillis() - begin;
  92.         logger.error(LogUtil.getMsg(new Object[] { RSP, className, methodName, rsp, Long.valueOf(space) }),
  93.                 new Object[] { t });
  94.         return rsp;
  95.     }

  96.     public void validBaseReq(Object... params) throws IllegalArgumentException, IllegalAccessException {
  97.         if (ArrayUtils.isEmpty(params)) {
  98.             return;
  99.         }
  100.         for (Object object : params) {
  101.             if (null == object) {
  102.                 throw new MyException(ResultCode.PARAM_ERROR, "Bean参数不能为null");
  103.             }
  104.             if (object.getClass().isArray()) {
  105.                 Object[] objectArr = (Object[]) object;
  106.                 if (ArrayUtils.isNotEmpty(objectArr)) {
  107.                     for (Object item : objectArr) {
  108.                         validBaseReq(new Object[] { item });
  109.                     }
  110.                 }
  111.             } else if ((object instanceof IBaseReq)) {
  112.                 ((IBaseReq) object).verify();
  113.                 BaseBeanValid.validField(object);
  114.             }
  115.         }
  116.     }
  117.    
  118. }
复制代码







像猪一样乐观,像鱼一样自由
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

Archiver|手机版|小黑屋|江苏金智教育信息股份有限公司 ( 系统管理员:binmeng@wisedu.com  

GMT+8, 2021-4-23 07:44

Powered by Discuz! X3.2

© 2015 Design: www.wisedu.com

快速回复 返回顶部 返回列表