|
本帖最后由 leizhou 于 2017-9-2 18:59 编辑
Springmvc中切面打印请求响应日志,捕获异常
------------------------------------------------------
目的:
通常不允许直接将异常堆栈信息显示在前端,而必须经过转义为通用性异常描述,以示友好,且避免安全漏洞问题。
利用Springmvc中切面,打印请求响应日志,捕获异常并转义为通用性异常描述。
------------------------------------------------------
1)常见做法
注:只能拦截controller请求,无法拦截soap请求。注:仅捕获异常并转义为通用性异常描述。
- @ControllerAdvice
- public class GlobalControllerAdvice {
- private LoggerDecorate LOGGER = LoggerDecorateFactory.getLogger(this.getClass());
- /**
- * 异常控制
- *
- */
- @ExceptionHandler(Exception.class)
- public ModelAndView defaultErrorHandler(HttpServletResponse resp, Exception e) throws Exception {
- LOGGER.error("执行异常:", e);
- String logId = MDC.get("logId");// 这里建议应该输出logId,以便到日志文件中定义异常
- if (HttpUtil.isAjaxRequest()) {
- Map<String, Object> model = new HashMap<>();
- model.put("logId", logId);
- model.put("msg", e instanceof MyException ? e.getMessage() : "系统异常");
- return new ModelAndView(new MappingJackson2JsonView(), model);
- }
- ModelAndView mav = new ModelAndView();
- mav.addObject("logId", logId);
- mav.addObject("msg", e instanceof MyException ? e.getMessage() : "系统异常");
- //mav.addObject("err", e);// 若输出异常堆栈信息,则页面显示不友好,且可能暴露安全漏洞
- mav.setViewName("error");// 自定义error页面,显示logId和msg
- return mav;
- }
复制代码
- /**
- * 判断是否ajax请求
- */
- public static boolean isAjaxRequest() throws Exception {
- return "XMLHttpRequest".equals(getRequest().getHeader("X-Requested-With"));
- }
复制代码
------------------------------------------------------
1)统一做法
注:利用自定义切面,拦截controller请求,和拦截webservice请求。
注:接口返回类型只支持JavaBean对象、Integer和String,不能为void.若是某些特殊类型,如Map,也需修改下面的代码进行非常简单的特殊处理。
注:使用springmvc自带的校验参数的注解@Valid,不支持webservice接口。建议自定义注解进行参数校验,见下面代码中的validBaseReq(arguments);
- /**
- * <pre>
- * 描述:切面记录请求日志,拦截各种接口。
- * </pre>
- * @author 01116253
- * @since 1.0
- */
- @Aspect
- @Component
- public class LogAspect {
- private static LoggerDecorate logger = LoggerDecorateFactory.getLogger(LogAspect.class);
- private static final String REQ = "REQ";
- private static final String RSP = "RSP";
- @Pointcut("execution(* com.wisedu.api.message.controller..*Controller.*(..))"
- + "|| execution(* com.wisedu.api.app.controller..*Controller.*(..))"
- + "|| execution(* com.wisedu.api.transfer.controller..*Controller.*(..))")
- public void api() {
- }
- @Around("api()")
- public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
- long begin = System.currentTimeMillis();
- Class<?> clazz = pjp.getSignature().getDeclaringType();
- String className = clazz.getName();
- String methodName = pjp.getSignature().getName();
- Object[] arguments = pjp.getArgs();
- try {
- // 打印请求日志
- logger.info(LogUtil.getMsg(REQ, className, methodName, arguments));
- } catch (MyException e) {
- logger.error("切面打印请求失败", e);
- } catch (Exception e) {
- logger.error("切面打印请求失败", e);
- }
- try {
- validBaseReq(arguments);
- // GlobalExceptionHandler统一异常处理类,无法拦截soap的顶层异常,因此在这里处理
- Object rsp = pjp.proceed();
- logger.info(className + " | " + methodName + " cost time:{}", (System.currentTimeMillis() - begin));
- return rsp;
- } catch (MyException e) {
- return getErrorResult(e.getStatus(), e.getMsg(), begin, clazz, className, methodName, arguments, e);
- } catch (Exception e) {
- return getErrorResult(ResultCode.OTHER_ERROR, "内部异常", begin, clazz, className, methodName, arguments, e);
- }
- }
- // 当接口调用失败后,返回一个异常对象
- private Object getErrorResult(int code, String msg, long begin, Class<?> clazz, String className, String methodName,
- Object[] arguments, Throwable t) {
- Object obj = null;
- Method[] allMethods = clazz.getDeclaredMethods();
- Method method = null;
- for (Method tmpMethod : allMethods) {
- if (methodName.equals(tmpMethod.getName())) {
- method = tmpMethod;
- break;
- }
- }
- if (null == method) {
- logger.info("the method [{}] has no return type", new Object[] { methodName });
- obj = new BaseResult();
- } else {
- Class<?> rt = method.getReturnType();
- if (null == rt) {
- logger.info("the method [{}] has no return type", new Object[] { methodName });
- obj = new BaseResult();
- } else {
- String rtName = StringUtils.trimToEmpty(rt.getName());
- if (rtName.endsWith("Integer")) {
- obj = Integer.valueOf(0);
- } else if (rtName.endsWith("String")) {
- obj = "error";
- } else {// TODO 返回类型的特殊处理
- try {
- obj = rt.newInstance();
- } catch (Exception e) {
- logger.error("the method [{}] return type newInstance err", new Object[] { methodName, e });
- obj = new BaseResult();
- }
- }
- }
- }
- if (((obj instanceof Integer)) || ((obj instanceof String))) {
- long space = System.currentTimeMillis() - begin;
- logger.error(LogUtil.getMsg(new Object[] { RSP, className, methodName, obj, Long.valueOf(space) }),
- new Object[] { t });
- return obj;
- }
- BaseResult rsp = (BaseResult) obj;
- rsp.setCode(code);
- rsp.setMsg(msg);
- long space = System.currentTimeMillis() - begin;
- logger.error(LogUtil.getMsg(new Object[] { RSP, className, methodName, rsp, Long.valueOf(space) }),
- new Object[] { t });
- return rsp;
- }
- public void validBaseReq(Object... params) throws IllegalArgumentException, IllegalAccessException {
- if (ArrayUtils.isEmpty(params)) {
- return;
- }
- for (Object object : params) {
- if (null == object) {
- throw new MyException(ResultCode.PARAM_ERROR, "Bean参数不能为null");
- }
- if (object.getClass().isArray()) {
- Object[] objectArr = (Object[]) object;
- if (ArrayUtils.isNotEmpty(objectArr)) {
- for (Object item : objectArr) {
- validBaseReq(new Object[] { item });
- }
- }
- } else if ((object instanceof IBaseReq)) {
- ((IBaseReq) object).verify();
- BaseBeanValid.validField(object);
- }
- }
- }
-
- }
复制代码
|
|