本文主要整理了使用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
@Component
public 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(), " ,");
}
}