| New file |
| | |
| | | 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() + "]"; |
| | | } |
| | | } |
| | | } |