• Spring框架下的 “接口调用、MVC请求” 调用参数、返回值、耗时信息输出


    主要拦截前端或后天的请求,打印请求方法参数、返回值、耗时、异常的日志。方便开发调试,能很快定位到问题出现在哪个方法中。

     

    前端请求拦截,mvc的拦截器

      1 import java.util.Date;
      2 import java.util.Iterator;
      3 import java.util.Map;
      4 import java.util.Set;
      5 
      6 import javax.servlet.http.HttpServletRequest;
      7 import javax.servlet.http.HttpServletResponse;
      8 
      9 import org.codehaus.jackson.map.ObjectMapper;
     10 import org.springframework.core.NamedThreadLocal;
     11 import org.springframework.web.servlet.HandlerInterceptor;
     12 import org.springframework.web.servlet.ModelAndView;
     13 
     14 import com.xxx.eduyun.sdk.log.ApplicationLogging;
     15 import com.xxx.flipclass.sdk.client.utils.TimeUtil;
     16 
     17 /**
     18  * <b>function:</b> spring mvc 请求拦截器
     19  * @author hoojo
     20  * @createDate 2016-11-24 下午3:19:27
     21  * @file MVCRequestInterceptor.java
     22  * @package com.xxx.eduyun.app.mvc.interceptor
     23  * @project eduyun-app-web
     24  * @blog http://blog.csdn.net/IBM_hoojo
     25  * @email hoojo_@126.com
     26  * @version 1.0
     27  */
     28 public class MVCRequestInterceptor extends ApplicationLogging implements HandlerInterceptor {
     29 
     30     private static final ObjectMapper mapper = new ObjectMapper();
     31     private NamedThreadLocal<Long>  startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-startTimed");  
     32     
     33     @Override
     34     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
     35         
     36         
     37         info("##############################【一个MVC完整请求开始】##############################");
     38         
     39         info("*******************MVC业务处理开始**********************");
     40         try {
     41             long timed = System.currentTimeMillis();
     42             startTimeThreadLocal.set(timed);
     43             
     44             String requestURL = request.getRequestURI();
     45             info("当前请求的URL:【{}】", requestURL);
     46             info("执行目标方法: {}", handler);
     47             
     48             Map<String, ?> params = request.getParameterMap();
     49             if (!params.isEmpty()) {
     50                 info("当前请求参数打印:");
     51                 print(request.getParameterMap(), "参数");
     52             }
     53         } catch (Exception e) {
     54             error("MVC业务处理-拦截器异常:", e);
     55         }
     56         info("*******************MVC业务处理结束**********************");
     57         
     58         return true;
     59     }
     60 
     61     @Override
     62     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
     63         
     64         info("*******************一个MVC 视图渲染开始**********************");
     65         
     66         try {
     67             info("执行业务逻辑代码耗时:【{}】", TimeUtil.formatTime(new Date().getTime() - startTimeThreadLocal.get()));
     68             String requestURL = request.getRequestURI();
     69             info("当前请求的URL:【{}】", requestURL);
     70             
     71             if (modelAndView != null) {
     72                 info("即将返回到MVC视图:{}", modelAndView.getViewName());
     73                 
     74                 if (modelAndView.getView() != null) {
     75                     info("返回到MVC视图内容类型ContentType:{}", modelAndView.getView().getContentType());
     76                 }
     77                 
     78                 if (!modelAndView.getModel().isEmpty()) {
     79                     
     80                     info("返回到MVC视图{}数据打印如下:", modelAndView.getViewName());
     81                     print(modelAndView.getModel(), "返回数据");
     82                 }
     83             }
     84         } catch (Exception e) {
     85             error("MVC 视图渲染-拦截器异常:", e);
     86         }
     87         
     88         info("*******************一个MVC 视图渲染结束**********************");
     89     }
     90 
     91     @Override
     92     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
     93         
     94         try {
     95             String requestURL = request.getRequestURI();
     96             info("MVC返回请求完成URL:【{}】", requestURL);
     97             info("MVC返回请求完成耗时:【{}】", TimeUtil.formatTime(new Date().getTime() - startTimeThreadLocal.get()));
     98             if (ex != null) {
     99                 info("MVC返回请求发生异常:", ex.getMessage());
    100                 error("异常信息如下:", ex);
    101             }
    102         } catch (Exception e) {
    103             error("MVC完成返回-拦截器异常:", e);
    104         }
    105         
    106         info("##############################【一个MVC完整请求完成】##############################");
    107     }
    108     
    109     private void print(Map<String, ?> map, String prefix) {
    110         if (map != null) {
    111             Set<String> keys = map.keySet();
    112             Iterator<String> iter = keys.iterator();
    113             while (iter.hasNext()) {
    114                 
    115                 String name = iter.next();
    116                 if (name.contains("org.springframework.validation.BindingResult")) {
    117                     continue;
    118                 }
    119                 
    120                 String value = "";
    121                 try {
    122                     value = mapper.writeValueAsString(map.get(name));
    123                 } catch (Exception e) {
    124                     error("转换参数【{}】发生异常:", name, e);
    125                 }
    126                 info("{} "{}": {}", prefix, name, value);
    127             }
    128         }
    129     }
    130 }
    View Code

    spring-mvc.xml增加配置内容

     1 <mvc:interceptors>
     2     <mvc:interceptor>
     3         <mvc:mapping path="/**"/>
     4         <mvc:exclude-mapping path="/exceltemplate/**" />
     5         <mvc:exclude-mapping path="/statics/**" />
     6         <mvc:exclude-mapping path="/global/**" />
     7         <mvc:exclude-mapping path="/denied/**" />
     8         <mvc:exclude-mapping path="/favicon.ico" />
     9         <mvc:exclude-mapping path="/index.jsp" /> 
    10         <bean class="com.xxx.eduyun.app.mvc.interceptor.MVCRequestInterceptor"/>
    11     </mvc:interceptor>
    12 </mvc:interceptors>
    View Code

    过滤静态资源,一些静态资源不需要拦截,在这里配置黑名单不让它进入拦截器。

    下面是sdk接口拦截器,用到spirng的aop的MethodIntercept

      1 package com.xxx.flipclass.sdk.framework.aop;
      2 
      3 import java.lang.reflect.Method;
      4 import java.util.Date;
      5 
      6 import org.aopalliance.intercept.MethodInterceptor;
      7 import org.aopalliance.intercept.MethodInvocation;
      8 import org.apache.logging.log4j.LogManager;
      9 import org.apache.logging.log4j.Logger;
     10 import org.codehaus.jackson.map.ObjectMapper;
     11 
     12 import com.xxx.flipclass.sdk.client.utils.ParameterNameUtils;
     13 import com.xxx.flipclass.sdk.client.utils.TimeUtil;
     14 
     15 /**
     16  * <b>function:</b> Spring 接口调用拦截器,主要拦截com.xxx.*.sdk.client对外接口
     17  * @author hoojo
     18  * @createDate 2016-11-24 下午5:39:57
     19  * @file ExecutionApiLogMethodInterceptor.java
     20  * @package com.xxx.eduyun.sdk.framework
     21  * @project eduyun-sdk-service
     22  * @blog http://blog.csdn.net/IBM_hoojo
     23  * @email hoojo_@126.com
     24  * @version 1.0
     25  */
     26 public class ExecutionApiLogMethodInterceptor implements MethodInterceptor {
     27 
     28     private Logger log = LogManager.getLogger(ExecutionApiLogMethodInterceptor.class);
     29     private static final ObjectMapper mapper = new ObjectMapper();
     30     
     31     @Override
     32     public Object invoke(MethodInvocation invocation) throws Throwable {
     33 
     34         info("************************************【接口调用拦截开始】*************************************");
     35         String targetName = invocation.getThis().getClass().getSimpleName();
     36         Method method = invocation.getMethod();
     37         String methodName = method.getName();
     38         
     39         info("系统开始执行方法:{}.{}", targetName, methodName);
     40         
     41         info("【{}.{}】方法参数打印如下:", targetName, methodName);
     42         Object[] args = invocation.getArguments();
     43         
     44         //printArgs(args, method, invocation.getThis().getClass());
     45         printArgs(args, method);
     46         
     47         try {
     48             long timed = System.currentTimeMillis();
     49             
     50             Object result = invocation.proceed();
     51 
     52             info("【{}.{}】方法执行完成,耗时:【{}】", targetName, methodName, TimeUtil.formatTime(new Date().getTime() - timed));
     53             info("【{}.{}】方法执行返回结果:{}", targetName, methodName, result);
     54             
     55             info("【{}.{}】方法返回数据打印如下:", targetName, methodName);
     56             printResult(result);
     57             info("************************************【接口调用拦截结束*】************************************");
     58             
     59             return result;
     60         } catch (Throwable throwable) {
     61             error("外部接口调用方法【{}.{}】异常:", targetName, methodName, throwable);
     62             
     63             info("************************************【接口异常拦截结束】*************************************");
     64             throw throwable;
     65         }
     66     }
     67     
     68     private void printArgs(Object[] args, Method method) {
     69         try {
     70             
     71             String[] argNames = null;
     72             try {
     73                 argNames = ParameterNameUtils.getMethodParamNames(method);
     74             } catch (Exception e) {
     75                 error("获取参数名称异常:", e);
     76             }
     77             
     78             if (args != null) {
     79                 for (int i = 0; i < args.length; i++) {
     80                     String argName = "";
     81                     if (argNames != null && argNames.length >= i) {
     82                         argName = argNames[i];
     83                     }
     84                     
     85                     if (args[i] != null) {
     86                         String value = "";
     87                         try {
     88                             value = mapper.writeValueAsString(args[i]);
     89                         } catch (Exception e) {
     90                             error("转换参数 "{}" 发生异常:", argName, e);
     91                         }
     92                         info("【参数 "{}" 】:({})", argName, value);
     93                     } else {
     94                         info("参数 "{}":NULL", argName);
     95                     }
     96                 }
     97             }
     98         } catch (Exception e) {
     99             error("【接口调用拦截器】打印方法执行参数异常:", e);
    100         }
    101     }
    102     
    103     private void printResult(Object result) {
    104         if (result != null) {
    105             try {
    106                 info("【返回数据】:({})", mapper.writeValueAsString(result));
    107             } catch (Exception e) {
    108                 error("返回数据打印异常:", e);
    109             }
    110         } else {
    111             info("【返回数据】:NULL");
    112         }
    113     }
    114     
    115     protected final void error(String msg, Object... objects) {
    116         log.error(msg, objects);
    117     }
    118 
    119     protected final void info(String msg, Object... objects) {
    120         log.info(msg, objects);
    121     }
    122 }
    View Code

    上面使用到了方法参数获取的工具类,代码如下:

      1 package com.xxx.flipclass.sdk.client.utils;
      2 
      3 import java.io.InputStream;
      4 import java.lang.reflect.Method;
      5 import java.lang.reflect.Modifier;
      6 import java.util.Arrays;
      7 
      8 import org.springframework.asm.ClassReader;
      9 import org.springframework.asm.ClassVisitor;
     10 import org.springframework.asm.ClassWriter;
     11 import org.springframework.asm.Label;
     12 import org.springframework.asm.MethodVisitor;
     13 import org.springframework.asm.Opcodes;
     14 import org.springframework.asm.Type;
     15 
     16 /**
     17  * <b>function:</b> 获取方法参加名称
     18  * @createDate 2016-11-25 下午3:40:33
     19  * @file ParameterNameUtils.java
     20  * @package com.xxx.flipclass.sdk.client.utils
     21  * @project flipclass-sdk-client
     22  * @version 1.0
     23  */
     24 public abstract class ParameterNameUtils {
     25 
     26     /**
     27      * 获取指定类指定方法的参数名
     28      *
     29      * @param clazz 要获取参数名的方法所属的类
     30      * @param method 要获取参数名的方法
     31      * @return 按参数顺序排列的参数名列表,如果没有参数,则返回null
     32      */
     33     public static String[] getMethodParamNames(Class<?> clazz, final Method method) throws Exception {
     34         
     35         try {
     36             
     37             final String[] paramNames = new String[method.getParameterTypes().length];
     38             String className = clazz.getName();
     39             
     40             int lastDotIndex = className.lastIndexOf(".");
     41             className = className.substring(lastDotIndex + 1) + ".class";
     42             InputStream is = clazz.getResourceAsStream(className);
     43             
     44             final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
     45             ClassReader cr = new ClassReader(is);
     46             
     47             cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
     48                 @Override
     49                 public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
     50                     final Type[] args = Type.getArgumentTypes(desc);
     51                     // 方法名相同并且参数个数相同
     52                     if (!name.equals(method.getName()) || !sameType(args, method.getParameterTypes())) {
     53                         return super.visitMethod(access, name, desc, signature, exceptions);
     54                     }
     55                     MethodVisitor v = cv.visitMethod(access, name, desc, signature, exceptions);
     56                     return new MethodVisitor(Opcodes.ASM4, v) {
     57                         @Override
     58                         public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
     59                             int i = index - 1;
     60                             // 如果是静态方法,则第一就是参数
     61                             // 如果不是静态方法,则第一个是"this",然后才是方法的参数
     62                             if (Modifier.isStatic(method.getModifiers())) {
     63                                 i = index;
     64                             }
     65                             if (i >= 0 && i < paramNames.length) {
     66                                 paramNames[i] = name;
     67                             }
     68                             super.visitLocalVariable(name, desc, signature, start, end, index);
     69                         }
     70                         
     71                     };
     72                 }
     73             }, 0);
     74             return paramNames;
     75         } catch (Exception e) {
     76             throw e;
     77         }
     78     }
     79     
     80     /**
     81      * 比较参数类型是否一致
     82      * @param types asm的类型({@link Type})
     83      * @param clazzes java 类型({@link Class})
     84      * @return
     85      */
     86     private static boolean sameType(Type[] types, Class<?>[] clazzes) {
     87         // 个数不同
     88         if (types.length != clazzes.length) {
     89             return false;
     90         }
     91 
     92         for (int i = 0; i < types.length; i++) {
     93             if (!Type.getType(clazzes[i]).equals(types[i])) {
     94                 return false;
     95             }
     96         }
     97         return true;
     98     }
     99 
    100     /**
    101      * 获取方法的参数名
    102      * @param Method
    103      * @return argsNames[]
    104      */
    105     public static String[] getMethodParamNames(final Method method) throws Exception {
    106         
    107         return getMethodParamNames(method.getDeclaringClass(), method);
    108     }
    109 
    110     public static void main(String[] args) throws Exception {
    111         Class<ParameterNameUtils> clazz = ParameterNameUtils.class;
    112         
    113         Method method = clazz.getDeclaredMethod("getMethodParamNames", Method.class);
    114         String[] parameterNames = ParameterNameUtils.getMethodParamNames(method);
    115         System.out.println(Arrays.toString(parameterNames));
    116         
    117         method = clazz.getDeclaredMethod("sameType", Type[].class, Class[].class);
    118         parameterNames = ParameterNameUtils.getMethodParamNames(method);
    119         System.out.println(Arrays.toString(parameterNames));
    120     }
    121 }
    View Code

    最后需要添加配置,拦截哪些接口或是实现类,具体看个人业务

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:aop="http://www.springframework.org/schema/aop"
     5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     6                         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
     7                         http://www.springframework.org/schema/aop 
     8                          http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
     9                         
    10 
    11     <bean id="externalApiMethodInterceptor" class="com.xxx.flipclass.sdk.framework.aop.ExecutionApiLogMethodInterceptor" />
    12 
    13     <aop:config proxy-target-class="true">
    14         <aop:pointcut id="externalApiMethodPointcut" expression="!execution(* com.xxx.flipclass.sdk.client.interfaces..*.loginInfoService.*(..)) and (execution(* com.xxx.*.sdk.client.interfaces..*.*Client*.*(..)) || execution(* com.xxx.*.sdk.client.interfaces..*.*Service*.*(..)))" />
    15         <aop:advisor advice-ref="externalApiMethodInterceptor" pointcut-ref="externalApiMethodPointcut" />
    16     </aop:config>
    17 </beans>
    View Code
  • 相关阅读:
    unity 编辑器 对比两次节点信息 查看新增节点和消失节点。
    根据模型的Height进行颜色的渐变 (Shader相关)
    TimeLine一些思考
    (unity小工具)C# 获取选择的Gameobject对象被引用的类名和字段名
    copy节点相对prefab的路径 (unity小工具)
    使用LineRender绘制网格线
    龙书11_chapter_6 二:HillsDemo解析
    龙书11_chapter_6 一:一般绘制流程
    龙书11_chapter_4 三:每一小节关键点
    龙书11_chapter_4 二:习题中的Adapter
  • 原文地址:https://www.cnblogs.com/hoojo/p/6108722.html
Copyright © 2020-2023  润新知