huangxz
2026-03-04 b574da11be6362f65f9569951637ebecffb8e2c4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.xiaopeng.automaticPilot.config;
 
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
 
import java.lang.reflect.Method;
import java.util.Arrays;
 
/**
 * Feign 调用日志切面
 *
 * <p>拦截 com.ros.feign 包下所有 Feign 接口方法,记录:
 * <ul>
 *   <li>调用的接口类名 + 方法名</li>
 *   <li>入参(JSON 序列化)</li>
 *   <li>出参(JSON 序列化)</li>
 *   <li>耗时(ms)</li>
 *   <li>异常信息(含完整堆栈)</li>
 * </ul>
 *
 * <p>同时覆盖任何实现了 Feign 接口的 bean(包括 SecureGatewayFeignService 等外部引入的客户端),
 * 通过第二个切点 {@code feignBeanMethods()} 拦截。
 */
@Slf4j
@Aspect
@Component
public class FeignLoggingAspect {
 
    /**
     * 切点1:com.ros.feign 包下所有类的所有方法
     * 覆盖 UserCenterFeign、RobotFeign、MappingServiceFeign、VmpRobotFeign 等
     */
    @Around("execution(* com.ros.feign..*(..))")
    public Object logRosFeignCall(ProceedingJoinPoint pjp) throws Throwable {
        return doLog(pjp);
    }
 
    /**
     * 切点2:所有带 @FeignClient 注解的接口的实现 bean 的所有方法
     * 覆盖 SecureGatewayFeignService 等外部包引入的 Feign 客户端
     */
    @Around("within(@org.springframework.cloud.openfeign.FeignClient *)")
    public Object logExternalFeignCall(ProceedingJoinPoint pjp) throws Throwable {
        return doLog(pjp);
    }
 
    private Object doLog(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        String className = pjp.getTarget().getClass().getSimpleName();
        String methodName = method.getName();
        String fullMethod = className + "#" + methodName;
 
        Object[] args = pjp.getArgs();
        String argsJson = safeToJson(args);
 
        log.info("[Feign] >>> 调用开始 | method={} | args={}", fullMethod, argsJson);
 
        long start = System.currentTimeMillis();
        try {
            Object result = pjp.proceed();
            long elapsed = System.currentTimeMillis() - start;
            String resultJson = safeToJson(result);
            log.info("[Feign] <<< 调用成功 | method={} | elapsed={}ms | result={}", fullMethod, elapsed, resultJson);
            return result;
        } catch (Throwable ex) {
            long elapsed = System.currentTimeMillis() - start;
            log.error("[Feign] <<< 调用异常 | method={} | elapsed={}ms | error={} | args={}",
                    fullMethod, elapsed, ex.getMessage(), argsJson, ex);
            throw ex;
        }
    }
 
    /**
     * 安全序列化为 JSON,避免序列化失败导致日志切面本身抛异常
     */
    private String safeToJson(Object obj) {
        if (obj == null) {
            return "null";
        }
        try {
            if (obj instanceof Object[]) {
                return JSON.toJSONString(Arrays.asList((Object[]) obj));
            }
            return JSON.toJSONString(obj);
        } catch (Exception e) {
            return "[序列化失败: " + obj.getClass().getSimpleName() + "]";
        }
    }
}