教你使用拦截器做日志记录(绝对不后悔篇)

430 阅读2分钟

在日常开发过程中,我们会打印各种各样的日志。那么如何记录一次请求的RT、方法的入参、结果。这边就想着怎么可以一劳永逸,不用在每个方法上打上日志的注解?同时,日志的格式还是json的形式存在。那就再方便不过了。

具体的流程如下:

最后决定使用方法拦截器实现。具体参考如下:

1、引入Maven坐标

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.24</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.9.6</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.72</version>
</dependency>

2、自定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceGuard {

    String module() default "";
}

3、日志接口

日志注解增强器

public class ServiceLoggerAnnotationHelper {

    // 日志模块名称
    private static final ConcurrentHashMap<Method, String> LOGGER_MODULE_NAMES = new ConcurrentHashMap<>();

    // 记录器没有注释
    private static final Set<Method> LOGGER_WITHOUT_ANNOTATIONS = new HashSet<>();

    public static Optional<String> getLoggerModuleName(Method method) {

        // 校验是否已经存在,存在直接返回
        String name = LOGGER_MODULE_NAMES.get(method);
        if (StringUtils.hasLength(name)) {
            return Optional.of(name);
        }

        if (LOGGER_WITHOUT_ANNOTATIONS.contains(method)) {
            return Optional.empty();
        }

        ServiceGuard annotation = method.getDeclaringClass().getAnnotation(ServiceGuard.class);
        if (null == annotation) {
            LOGGER_WITHOUT_ANNOTATIONS.add(method);
            return Optional.empty();
        }

        name = annotation.module();
        LOGGER_MODULE_NAMES.putIfAbsent(method, name);
        return Optional.of(name);
    }

}

日志接口

public interface IServiceLogger<T> {

    /**
     * 异常日志处理
     *
     * @param e 异常
     * @return 结果
     */
    T failedException(Throwable e) throws Throwable;

    Class<T> getResultClass();

    String getResultCode(Object result);

    String getResultMessage(Object result);

    String getLoggerTitle(Method method);

    IServiceLogger DEFAULT_LOGGER_HELPER = new IServiceLogger() {
        @Override
        public Object failedException(Throwable e) throws Throwable {
            throw e;
        }

        @Override
        public Class getResultClass() {
            return Object.class;
        }

        @Override
        public String getResultCode(Object result) {
            return "";
        }

        @Override
        public String getResultMessage(Object result) {
            return "";
        }

        @Override
        public String getLoggerTitle(Method method) {
            Optional<String> name = ServiceLoggerAnnotationHelper.getLoggerModuleName(method);
            return name.orElse("INNER_SERVICE");
        }
    };

}

服务Response

@Data
public class ServiceResult<T> implements Serializable {
    protected boolean isSuccess;
    protected String code;
    protected String message;

    protected T result;

    public static <T> ServiceResult<T> failed(String errorCode, String errorMsg) {
        ServiceResult<T> result = new ServiceResult<>();
        result.isSuccess = false;
        result.code = errorCode;
        result.message = errorMsg;
        return result;
    }

}

实现类

public class ServiceResultLogger implements IServiceLogger<ServiceResult> {
    @Override
    public ServiceResult failedException(Throwable e) throws Throwable {
        return ServiceResult.failed(e.getClass().getCanonicalName(), e.getLocalizedMessage());
    }

    @Override
    public Class getResultClass() {
        return ServiceResult.class;
    }

    @Override
    public String getResultCode(Object result) {
        return ((ServiceResult)result).getCode();
    }

    @Override
    public String getResultMessage(Object result) {
        return ((ServiceResult)result).getMessage();
    }

    @Override
    public String getLoggerTitle(Method method) {
        Optional<String> name = ServiceLoggerAnnotationHelper.getLoggerModuleName(method);
        return name.orElse("SERVICE");
    }
}

4、方法拦截器

public class ServiceInterceptor implements MethodInterceptor {

    private static final Logger logger = LoggerFactory.getLogger("RUNTIME_LOG");

    private final Map<Class<?>, IServiceLogger> loggerHelpers = new HashMap<>();

    private List<IServiceLogger> serviceLoggers;

    @PostConstruct
    public void init() {
        serviceLoggers.forEach(o -> {
            loggerHelpers.put(o.getResultClass(), o);
        });
    }

    public void setServiceLoggers(List<IServiceLogger> serviceLoggers) {
        this.serviceLoggers = serviceLoggers;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        // 获取对应的方法
        Method method = invocation.getMethod();

        IServiceLogger<?> loggerHelper = loggerHelpers.get(method.getReturnType());

        if (null == loggerHelper) {
            loggerHelper = IServiceLogger.DEFAULT_LOGGER_HELPER;
        }

        // 服务名
        String serviceName = method.getDeclaringClass().getCanonicalName();

        // 方法名
        String methodName = method.getName();

        // 开始时间
        long startTime = System.currentTimeMillis();

        // 调用结果
        Object result = null;

        // 异常信息
        Throwable exception = null;
        try {
            result = invocation.proceed();
            return result;
        } catch (Throwable e) {
            exception = e;
            return result;
        } finally {
            // 打印日志
            String errorCode;
            String errorMsg;
            if (null == result || IServiceLogger.DEFAULT_LOGGER_HELPER.equals(loggerHelper)
                || !loggerHelper.getResultClass().isAssignableFrom(result.getClass())) {
                errorCode = null == exception ? "SUCCESS" : exception.getClass().getCanonicalName();
                errorMsg = null == exception ? "SUCCESS" : exception.getMessage();
            } else {
                errorCode = loggerHelper.getResultCode(result);
                errorMsg = loggerHelper.getResultMessage(result);
            }
            if (null == exception) {
                logger.info("|" + loggerHelper.getLoggerTitle(method) + "|{}|{}|{}|{}|{}|args:{}|result:{}|", serviceName,
                    methodName, System.currentTimeMillis() - startTime, errorCode, errorMsg,
                    JSON.toJSONString(invocation.getArguments()), JSON.toJSONString(result));
            } else {
                logger.error("|" + loggerHelper.getLoggerTitle(method) + "|" + serviceName + "|" + methodName + "|" + (
                    System.currentTimeMillis() - startTime) + "|" + errorCode + "|" + errorMsg + "|args:"
                    + JSON.toJSONString(invocation.getArguments()) + "|result:" + JSON.toJSONString(result), exception);
            }
        }

    }
}

5、Spring统一管理(CommonAutoConfiguration)

@Configuration
public class CommonAutoConfiguration {

    @Bean
    public ServiceInterceptor serviceInterceptor(@Autowired List<IServiceLogger> serviceLoggers) {
        ServiceInterceptor interceptor = new ServiceInterceptor();
        interceptor.setServiceLoggers(serviceLoggers);
        return interceptor;
    }

    @Bean
    public ServiceResultLogger serviceResultLogger() {
        return new ServiceResultLogger();
    }

    @Bean
    public Advisor walletLoggerInterceptor(@Autowired ServiceInterceptor serviceInterceptor) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        StringJoiner sj = new StringJoiner(" or ");
        sj.add("@within(com.example.demo.ServiceGuard)");
        pointcut.setExpression(sj.toString());
        return new DefaultPointcutAdvisor(pointcut, serviceInterceptor);
    }
}

6、测试结果

@RestController
@RequestMapping("/test")
// 打上我们自己的注解即可
@ServiceGuard(module = "aaa")
public class TestCtrl {
    @RequestMapping("/a")
    public ServiceResult<AModel> a(@RequestBody AModel model){
        return ServiceResult.failed("a","错误");
    }
}

结果截图:

7、结合SLS日志

附上源码地址:gitee.com/lby6220/log…