package com.example.demo.config;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.slf4j.MDC;import org.springframework.core.DefaultParameterNameDiscoverer;import org.springframework.core.ParameterNameDiscoverer;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.util.*;@Aspect@Componentpublic class LogAspect { private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class); /** * 定义一个切点 */ @Pointcut("execution(public * com..*.controller..*Controller.*(..))") public void controllerPointcut() { } @Before("controllerPointcut()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 日志编号 MDC.put("UUID", // fixme 抽取方法 UUID.randomUUID().toString().replace("-", "")); // 开始打印请求日志 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); Signature signature = joinPoint.getSignature(); // 调用的 controller 方法名 String name = signature.getName(); // 打印业务操作 String nameCn = ""; // 根据方法名识别业务操作 if (name.contains("list") || name.contains("query")) { nameCn = "查询"; } else if (name.contains("save")) { nameCn = "保存"; } else if (name.contains("delete")) { nameCn = "删除"; } else { nameCn = "操作"; } // 使用反射,获取业务名称 Class clazz = signature.getDeclaringType(); Field field; String businessName = ""; try { // 需要在 controller 中定义该字段 field = clazz.getField("BUSINESS_NAME"); if (!StringUtils.isEmpty(field)) { businessName = (String) field.get(clazz); } } catch (NoSuchFieldException e) { LOG.error("未获取到业务名称"); } catch (SecurityException e) { LOG.error("获取业务名称失败", e); } // 打印请求信息 LOG.info("------------- 【{}】{}开始 -------------", businessName, nameCn); LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod()); LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name); LOG.info("远程地址: {}", request.getRemoteAddr()); // 打印请求参数 Object[] args = joinPoint.getArgs(); // 这里获取到所有的参数值的数组 MethodSignature methodSignature = (MethodSignature) signature; // 最关键的一步:通过这获取到方法的所有参数名称的字符串数组 String[] parameterNames = methodSignature.getParameterNames(); Map<String, Object> argMap = new HashMap<>(args.length); for (int i=0; i<args.length; i++) { // 去除非必要参数 if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) { continue; } argMap.put(parameterNames[i], args[i]); } // todo 去除请求中的敏感字段 String[] excludeProperties = {"password"}; for (String key: excludeProperties) { argMap.remove(key); } LOG.info("请求参数: {}, 其中去除了敏感字段: {}", argMap, Arrays.asList(excludeProperties)); } @Around("controllerPointcut()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); // 排除字段,敏感字段或太长的字段不显示 String[] excludeProperties = {"password", "shard"}; // todo 去除 结果集的敏感内容? LOG.info("返回结果: {}", result); LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime); return result; }}