本文主要整理了使用AOP切面获取接口调用时的入参出参并打印相关日志的过程,修改打印范围可以通过修改@Around注解中的包名来修改,以下是实现代码:

    1. package com.chongtong.aop;
    2. import com.google.gson.Gson;
    3. import javassist.*;
    4. import javassist.bytecode.*;
    5. import org.apache.commons.lang3.*;
    6. import org.aspectj.lang.JoinPoint;
    7. import org.aspectj.lang.ProceedingJoinPoint;
    8. import org.aspectj.lang.annotation.*;
    9. import org.slf4j.Logger;
    10. import org.slf4j.LoggerFactory;
    11. import org.springframework.stereotype.Component;
    12. import org.springframework.web.context.request.*;
    13. import javax.servlet.http.HttpServletRequest;
    14. import java.time.*;
    15. import java.time.format.DateTimeFormatter;
    16. @Aspect
    17. @Component
    18. public class LogAop {
    19. private Logger logger = LoggerFactory.getLogger(LogAop.class);
    20. private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    21. /**
    22. * @param pjp
    23. * @return
    24. * @throws Throwable
    25. * @Title:doAround
    26. * @Description: 环绕触发
    27. */
    28. @Around("execution(* com.chongtong.web..*.*(..))")
    29. public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
    30. RequestAttributes ra = RequestContextHolder.getRequestAttributes();
    31. ServletRequestAttributes sra = (ServletRequestAttributes) ra;
    32. HttpServletRequest request = sra.getRequest();
    33. String inputParam = getMethodParams(pjp);
    34. // 获取请求地址
    35. String requestPath = request.getRequestURI();
    36. Instant startTime = Instant.now();
    37. // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
    38. Object obj = pjp.proceed();
    39. // result的值就是被拦截方法的返回值
    40. String result = obj instanceof String ? (String) obj : new Gson().toJson(obj);
    41. Instant endTime = Instant.now();
    42. String optTime = startTime.atZone(ZoneId.systemDefault()).format(dtf);
    43. long pro_time = Duration.between(startTime, endTime).toMillis();
    44. StringBuffer sb = new StringBuffer();
    45. sb.append("\n 用户名:").append("todo").append("\n 访问接口地址:").append(requestPath)
    46. .append("\n 开始请求的时间:").append(optTime).append("\n 接口耗时:").append(pro_time).append("ms")
    47. .append("\n 请求参数:{\"request\":").append(inputParam).append("}")
    48. .append("\n 响应参数:{\"result\":").append(result).append("}");
    49. logger.info(sb.toString());
    50. return obj;
    51. }
    52. /**
    53. * 打印类method的名称以及参数
    54. *
    55. * @param point 切面
    56. */
    57. public String getMethodParams(JoinPoint point) {
    58. if (point == null) {
    59. return null;
    60. }
    61. //Signature 包含了方法名、申明类型以及地址等信息
    62. String class_name = point.getTarget().getClass().getName();
    63. String method_name = point.getSignature().getName();
    64. // 重新定义日志
    65. logger = LoggerFactory.getLogger(point.getTarget().getClass());
    66. //获取方法的参数值数组。
    67. Object[] method_args = point.getArgs();
    68. try {
    69. //获取方法参数名称
    70. String[] paramNames = getFieldsName(class_name, method_name);
    71. //打印方法的参数名和参数值
    72. return logParam(paramNames, method_args);
    73. } catch (Exception e) {
    74. e.printStackTrace();
    75. }
    76. return null;
    77. }
    78. /**
    79. * 使用javassist来获取方法参数名称
    80. *
    81. * @param class_name 类名
    82. * @param method_name 方法名
    83. * @return
    84. * @throws Exception
    85. */
    86. private String[] getFieldsName(String class_name, String method_name) throws Exception {
    87. Class<?> clazz = Class.forName(class_name);
    88. String clazz_name = clazz.getName();
    89. ClassPool pool = ClassPool.getDefault();
    90. ClassClassPath classPath = new ClassClassPath(clazz);
    91. pool.insertClassPath(classPath);
    92. CtClass ctClass = pool.get(clazz_name);
    93. CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);
    94. MethodInfo methodInfo = ctMethod.getMethodInfo();
    95. CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
    96. LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
    97. .getAttribute(LocalVariableAttribute.tag);
    98. if (attr == null) {
    99. return null;
    100. }
    101. String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
    102. int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
    103. for (int i = 0; i < paramsArgsName.length; i++) {
    104. paramsArgsName[i] = attr.variableName(i + pos);
    105. }
    106. return paramsArgsName;
    107. }
    108. /**
    109. * 判断是否为基本类型:包括String
    110. *
    111. * @param clazz clazz
    112. * @return true:是; false:不是
    113. */
    114. private boolean isPrimite(Class<?> clazz) {
    115. if (clazz.isPrimitive() || clazz == String.class) {
    116. return true;
    117. } else {
    118. return false;
    119. }
    120. }
    121. /**
    122. * 打印方法参数值 基本类型直接打印,非基本类型需要转成json字符串方法
    123. *
    124. * @param paramsArgsName 方法参数名数组
    125. * @param paramsArgsValue 方法参数值数组
    126. * @return
    127. */
    128. private String logParam(String[] paramsArgsName, Object[] paramsArgsValue) {
    129. if (ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)) {
    130. logger.info("该方法没有参数");
    131. return null;
    132. }
    133. StringBuffer buffer = new StringBuffer();
    134. for (int i = 0; i < paramsArgsName.length; i++) {
    135. // 参数名
    136. String name = paramsArgsName[i];
    137. // 参数值
    138. Object value = paramsArgsValue[i];
    139. buffer.append(name + " = ");
    140. if (isPrimite(value.getClass())) {
    141. buffer.append(value + " ,");
    142. } else {
    143. buffer.append(new Gson().toJson(value) + " ,");
    144. }
    145. }
    146. return StringUtils.removeEndIgnoreCase(buffer.toString(), " ,");
    147. }
    148. }