Spring Web 日志记录切面

发布时间 2023-06-05 21:43:06作者: ayiZzzz

Spring Web 日志记录切面

应用: 在我们进行 rest 接口编写时需要对该接口的耗时、参数、请求路径、返回值进行对应的记录

日志注解

把日志封装成注解的形式可以更好的供使用者使用,同时也利于解耦合

  • 代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MLog {
    
    // 是否记录输入参数
    boolean input() default true;

    // 是否记录输出参数
    boolean output() default true;
}

切面类

  • 利用 Spring AOP 机制对日志注解进行处理
@Aspect
public class MLogPrintAspect {

    @Around("@within(com.ayi.annotation.MLog) || @annotation(com.ayi.annotation.MLog)")
    public Object printMLog(ProceedingJoinPoint joinPoint) throws Throwable {
        // 切面开始时间
        long startTime = SystemClock.now();
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        Logger log = LoggerFactory.getLogger(methodSignature.getDeclaringType());
        String beginTime = DateUtil.now();
        // 方法返回结果
        Object result = null;
        try {
            result = joinPoint.proceed();
        } finally {
            // 获取目标方法
            Method targetMethod = joinPoint.getTarget().getClass().getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
            // 获取日志
            MLog mLog = Optional.ofNullable(targetMethod.getAnnotation(MLog.class)).orElse(joinPoint.getTarget().getClass().getAnnotation(MLog.class));

            if (null != mLog) {
                MLogPrint mLogPrint = new MLogPrint();
                mLogPrint.setBeginTime(beginTime);
                if (mLog.input()) {
                    mLogPrint.setInputParams(buildInput(joinPoint , targetMethod));
                }
                if (mLog.output()) {
                    mLogPrint.setOutputParams(result);
                }

                ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                Preconditions.checkNotNull(servletRequestAttributes , "servletRequestAttributes is not null");
                HttpServletRequest request;
                String requestURI = "";
                String methodType = "";
                try {
                    request = servletRequestAttributes.getRequest();
                    requestURI = request.getRequestURI();
                    /**
                        处理请求 URI 前缀 http://localhost:8080/test
                        /test
                     */
                    requestURI = CharSequenceUtil.removeSuffix(requestURI, URLUtil.url(requestURI).getPath());
                    methodType = request.getMethod();
                }catch (Exception ignored) {
                }
                // 打印日志
                log.info("[{}] {}, executeTime: {}ms, info: {}", methodType, requestURI, SystemClock.now() - startTime, JSON.toJSONString(mLogPrint));
            }
        }
        return result;

    }

    /**
     * 输入日志集合处理
     * @param joinPoint
     * @param method
     * @return
     */
    public Object[] buildInput(ProceedingJoinPoint joinPoint , Method method) {
        Object[] args = joinPoint.getArgs();
        Object[] printArgs = new Object[args.length];
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < args.length; i++) {
            if ((args[i] instanceof HttpServletRequest) || args[i] instanceof HttpServletResponse) {
                continue;
            }
            if (args[i] instanceof byte[]) {
                printArgs[i] = "byte array";
            } else if (args[i] instanceof MultipartFile) {
                printArgs[i] = "file";
            } else {
                printArgs[i] = args[i];
            }
        }
        return printArgs;
    }

    @Data
    private class MLogPrint {

        private String beginTime;

        private Object[] inputParams;

        private Object outputParams;
    }

}

封装 LogConfig 并进行 Spring Boot 模块封装

public class LogAutoConfiguration {

    @ConditionalOnMissingBean
    @Bean
    MLogPrintAspect mLogPrintAspect() {
        return new MLogPrintAspect();
    }
}

注意: 在 resource/META-INFO/spring.factories 里面进行相关配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ayi.config.LogAutoConfiguration