本文主要整理了使用AOP切面获取接口调用时的入参出参并打印相关日志的过程,修改打印范围可以通过修改@Around注解中的包名来修改,以下是实现代码:
package com.chongtong.aop;import com.google.gson.Gson;import javassist.*;import javassist.bytecode.*;import org.apache.commons.lang3.*;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.context.request.*;import javax.servlet.http.HttpServletRequest;import java.time.*;import java.time.format.DateTimeFormatter;@Aspect@Componentpublic class LogAop {private Logger logger = LoggerFactory.getLogger(LogAop.class);private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");/*** @param pjp* @return* @throws Throwable* @Title:doAround* @Description: 环绕触发*/@Around("execution(* com.chongtong.web..*.*(..))")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {RequestAttributes ra = RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra = (ServletRequestAttributes) ra;HttpServletRequest request = sra.getRequest();String inputParam = getMethodParams(pjp);// 获取请求地址String requestPath = request.getRequestURI();Instant startTime = Instant.now();// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行Object obj = pjp.proceed();// result的值就是被拦截方法的返回值String result = obj instanceof String ? (String) obj : new Gson().toJson(obj);Instant endTime = Instant.now();String optTime = startTime.atZone(ZoneId.systemDefault()).format(dtf);long pro_time = Duration.between(startTime, endTime).toMillis();StringBuffer sb = new StringBuffer();sb.append("\n 用户名:").append("todo").append("\n 访问接口地址:").append(requestPath).append("\n 开始请求的时间:").append(optTime).append("\n 接口耗时:").append(pro_time).append("ms").append("\n 请求参数:{\"request\":").append(inputParam).append("}").append("\n 响应参数:{\"result\":").append(result).append("}");logger.info(sb.toString());return obj;}/*** 打印类method的名称以及参数** @param point 切面*/public String getMethodParams(JoinPoint point) {if (point == null) {return null;}//Signature 包含了方法名、申明类型以及地址等信息String class_name = point.getTarget().getClass().getName();String method_name = point.getSignature().getName();// 重新定义日志logger = LoggerFactory.getLogger(point.getTarget().getClass());//获取方法的参数值数组。Object[] method_args = point.getArgs();try {//获取方法参数名称String[] paramNames = getFieldsName(class_name, method_name);//打印方法的参数名和参数值return logParam(paramNames, method_args);} catch (Exception e) {e.printStackTrace();}return null;}/*** 使用javassist来获取方法参数名称** @param class_name 类名* @param method_name 方法名* @return* @throws Exception*/private String[] getFieldsName(String class_name, String method_name) throws Exception {Class<?> clazz = Class.forName(class_name);String clazz_name = clazz.getName();ClassPool pool = ClassPool.getDefault();ClassClassPath classPath = new ClassClassPath(clazz);pool.insertClassPath(classPath);CtClass ctClass = pool.get(clazz_name);CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);MethodInfo methodInfo = ctMethod.getMethodInfo();CodeAttribute codeAttribute = methodInfo.getCodeAttribute();LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);if (attr == null) {return null;}String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;for (int i = 0; i < paramsArgsName.length; i++) {paramsArgsName[i] = attr.variableName(i + pos);}return paramsArgsName;}/*** 判断是否为基本类型:包括String** @param clazz clazz* @return true:是; false:不是*/private boolean isPrimite(Class<?> clazz) {if (clazz.isPrimitive() || clazz == String.class) {return true;} else {return false;}}/*** 打印方法参数值 基本类型直接打印,非基本类型需要转成json字符串方法** @param paramsArgsName 方法参数名数组* @param paramsArgsValue 方法参数值数组* @return*/private String logParam(String[] paramsArgsName, Object[] paramsArgsValue) {if (ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)) {logger.info("该方法没有参数");return null;}StringBuffer buffer = new StringBuffer();for (int i = 0; i < paramsArgsName.length; i++) {// 参数名String name = paramsArgsName[i];// 参数值Object value = paramsArgsValue[i];buffer.append(name + " = ");if (isPrimite(value.getClass())) {buffer.append(value + " ,");} else {buffer.append(new Gson().toJson(value) + " ,");}}return StringUtils.removeEndIgnoreCase(buffer.toString(), " ,");}}
