Spring Boot AOP 实现接口日志

AOP(Aspect Oriented Programing)面向切面编程是通过预编译方式或者是运行期期间动态代理实现功能扩展而不用修改源代码通过AOP技术,实现一种通用的逻辑解耦,解决一些系统层面的问题,如日志,事务,权限等,从而实现高可用的可重用性和可维护性AOP的设计原理和思想.

需求: 记录FeignClient接口的输入输出日志

1. 首先要加载pom文件

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 写一个Aspect, 注意要加入注解@Aspect和@Component

@Aspect
@Component
public class FeignLoggingAspect {

    private final Environment env;
    private final Gson gson = new Gson();

    public FeignLoggingAspect(Environment env) {
        this.env = env;
    }

    /**
     * Pointcut that matches all feign client.
     */
    @Pointcut("@within(org.springframework.cloud.openfeign.FeignClient)")
    public void feignClientPointcut() {
        // Method is empty as this is just a Pointcut, the implementations are in the advices.
    }

    /**
     * Advice that logs when a method is entered and exited.
     *
     * @param joinPoint join point for advice
     * @return result
     * @throws Throwable throws IllegalArgumentException
     */
    @Around("feignClientPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        String clientName = getClientName(joinPoint);
        String requestUrl = getRequestUrl(signature);
        String traceId = UUID.randomUUID().toString();
        Logger log = LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringType());
        log.info("traceId: {}, Enter Feign Client: {} {} with argument[s] = {}", traceId, clientName, requestUrl,
            gson.toJson(joinPoint.getArgs()));
        try {
            long start = System.currentTimeMillis();
            Object result = joinPoint.proceed();
            long end = System.currentTimeMillis();
            log.info("traceId: {},Exit Feign Client[{}ms]: {}, {} with result = {}",
                traceId,
                end - start,
                clientName,
                requestUrl,
                gson.toJson(Optional.ofNullable(result).orElse("NULL")));
            return result;
        } catch (IllegalArgumentException e) {
            log.error("traceId: {},error call params: {}, {} with argument[s] = {}", traceId, clientName, requestUrl,
                gson.toJson(joinPoint.getArgs()));
            throw e;
        }
    }

    private String getClientName(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        Class type = signature.getDeclaringType();
        String clientName = type.getSimpleName();
        if (type.isAnnotationPresent(FeignClient.class)) {
            FeignClient client = (FeignClient) type.getAnnotation(FeignClient.class);
            if (StringUtils.isNotBlank(client.name())) {
                clientName = client.name();
            }
        }
        return clientName;
    }

    private String getRequestUrl(Signature signature) {
        String requestUrl = signature.getName();
        if (signature instanceof MethodSignature) {
            MethodSignature methodSignature = (MethodSignature) signature;
            Method targetMethod = methodSignature.getMethod();
            if (targetMethod.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping mapper = targetMethod.getAnnotation(RequestMapping.class);
                requestUrl = Arrays.toString(mapper.value());
            }
        }
        return requestUrl;
    }
}

然后启动程序,就能看到feign client 接口的输入输出日志了
 


版权声明:本文为marvin198801原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。