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 }
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>
过滤静态资源,一些静态资源不需要拦截,在这里配置黑名单不让它进入拦截器。
下面是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 }
上面使用到了方法参数获取的工具类,代码如下:
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 }
最后需要添加配置,拦截哪些接口或是实现类,具体看个人业务
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>
出处:
blog:http://blog.csdn.net/IBM_hoojo
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权所有,转载请注明出处 本文出自:
版权所有,欢迎转载,转载请注明出处,谢谢
赞
收藏
关注
评论