Recording a user request parameters using AOP

Due

In a production environment, encounter an error, by looking back, you can find the interface or method error, but if there is no argument, you can not reproduce the problem, so that the recording parameters of the request, it is important to repeat the question

demand

1, the method and parameters of the print request
2, and associated user passwords List type (long) printing parameters are not

Implementation process

1, adding aop class, use the execution mode section

@Slf4j
@Aspect
@Component
public class ControllerLog {
    private static final ThreadLocal<Long> START_TIME_THREAD_LOCAL =
            new NamedThreadLocal<>("ThreadLocal StartTime");

    private static final ThreadLocal<Integer> ID_THREAD_LOCAL =
            new NamedThreadLocal<>("ThreadLocal ID");

    private static final ThreadLocal<String> LOG_PREFIX_THREAD_LOCAL =
            new NamedThreadLocal<>("ThreadLocal LogPrefix");

    /**
     * <li>Before       : 在方法执行前进行切面</li>
     * <li>execution    : 定义切面表达式</li>
     * <p>public * com.example.javadevelopmentframework.javadevelopmentframework.controller..*.*(..))
     * <li>public :匹配所有目标类的public方法,不写则匹配所有访问权限</li>
     * <li>第一个* :方法返回值类型,*代表所有类型 </li>
     * <li>第二个* :包路径的通配符</li>
     * <li>第三个..* :表示impl这个目录下所有的类,包括子目录的类</li>
     * <li>第四个*(..) : *表示所有任意方法名,..表示任意参数</li>
     * </p>
     *
     * @param
     */
    @Pointcut("execution(public * XXX.XXX.XXX.*.*.controller..*.*(..))")
    public void exectionMethod() {
    }


    @Before("exectionMethod()")
    public void doBefore(JoinPoint joinPoint) {
        START_TIME_THREAD_LOCAL.set(System.currentTimeMillis());
        // 接收到请求,记录请求内容
        StringBuilder argsDes = new StringBuilder();
        //获取方法名
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        String logPrefix = className + "." + methodName + "()";
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        Method method = signature.getMethod();
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] paramNames = u.getParameterNames(method);
        if (args != null && paramNames != null) {
            argsDes.append(LogUtil.getMethodParams(signature, args, paramNames));
        }
        LOG_PREFIX_THREAD_LOCAL.set(logPrefix);
        int id = RandomUtil.randomInt();
        ID_THREAD_LOCAL.set(id);
        log.info("id={} =========开始请求:{},参数:{}", id, logPrefix, argsDes.toString());
    }

    @AfterReturning(pointcut = "exectionMethod()", returning = "rtn")
    public Object doAfter(Object rtn) {
        long endTime = System.currentTimeMillis();
        long begin = START_TIME_THREAD_LOCAL.get();
        if (rtn instanceof R) {
            R result = (R) rtn;
            if (result.getErrcode() != HttpStatus.OK.value()) {
                log.warn("请求异常:{},响应结果:{}", getLogPrefix(), rtn);
            }
        }
        log.info("id={} =========结束请求:{},耗时:{} ms", ID_THREAD_LOCAL.get(), getLogPrefix(), endTime - begin);
        destroyThreadLocal();
        return rtn;
    }

    private static String getLogPrefix() {
        return LOG_PREFIX_THREAD_LOCAL.get();
    }

    private static void destroyThreadLocal() {
        START_TIME_THREAD_LOCAL.remove();
        LOG_PREFIX_THREAD_LOCAL.remove();
        ID_THREAD_LOCAL.remove();
    }
}

2, LogUtil.getMethodParams implemented method

	public static StringBuilder getMethodParams(MethodSignature signature, Object[] args, String[] paramNames) {
        StringBuilder params = new StringBuilder();
        //密码类型的信息,不打印出来
        PasswordType passwordType = signature.getMethod().getAnnotation(PasswordType.class);
        if (null == passwordType) {
            for (int i = 0; i < args.length; i++) {
                //参数类型为List的参数也不打印值
                if (args[i] instanceof List) {
                    params.append(paramNames[i]).append("=").append("【List类型的参数不打印】").append(",");
                } else {
                    params.append(paramNames[i]).append("=").append(args[i]).append(",");
                }

            }
        } else {
            String[] fieldNames = passwordType.fieldNames();
            for (int i = 0; i < args.length; i++) {
                Map<String, Object> map = JsonUtil.objectToMap(args[i]);
                for (String fieldName : fieldNames) {
                    if (map != null && map.containsKey(fieldName) && null != map.get(fieldName)) {
                        map.put(fieldName, "*");
                    }
                }
                params.append(paramNames[i]).append("=").append(map).append(",");
            }
        }
        return params;
    }

3, @PasswordType notes, default password, but there may be other name

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PasswordType {
    String[] fieldNames() default {"password"};
}

4, @ PasswordType uses examples
to change the password, json parameter passing

	@PasswordType(fieldNames = {"password","newPassword"})
    @PostMapping("/editPwd")
    public R changePwd(@Valid @RequestBody UserChangePwdParamDTO dto) {
       
    }

effect

04-09 15:24:12.245 INFO  [xxx.xxx.xxx.aop.ControllerLog] - id=1638566979 =========开始请求:XXXXController.getSetting(),参数:yxH=10572,
04-09 15:24:12.255 INFO  [xxx.xxx.xxx..aop.ControllerLog] - id=1638566979 =========结束请求:XXXXController.getSetting(),耗时:11 ms

Released four original articles · won praise 0 · Views 35

Guess you like

Origin blog.csdn.net/m0_37453314/article/details/105411212