LogAdvice

简介: `LogAdvice` 类是用于日志记录的 AOP(面向切面编程)组件。它定义了在带有 `@Log` 注解的方法执行前后进行操作的切点。在方法调用前,它记录请求开始时间、描述、URL、参数和 headers。方法成功返回后,记录请求的执行时间和响应。类还包含一些辅助方法,如判断是否为 Feign 请求或控制器请求,并获取请求的相关信息。
package integration.advice;

import cn.hutool.core.util.ReflectUtil;
import integration.annotation.Log;
import integration.exception.NormalBreakException;
import integration.util.Base64Utils;
import integration.util.HttpRequestUtils;
import integration.util.LoginUtil;
import integration.util.RequestThreadLocal;
import integration.util.TryCatchUtil;
import feign.Target;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static integration.configuration.CommonConfig.isPrd;
import static integration.constant.ThreadConstants.START_TIME;
import static integration.util.AnnotationUtil.*;
import static integration.util.ErrorLogRecord.recordException;

@Component
@Aspect
@Order(110)
@Slf4j
public class LogAdvice {

    private static final String[] ERROR_SUFFIX_URL = new String[]{"not postMapping or getMapping!"};
    private static final String FEIGN_TARGET_CLASS_NAME = "HardCodedTarget";
    private static final String FIELD_TARGET = "target";

    @Pointcut("@within(com.ph.sp.integration.annotation.Log)")
    public void logCut() {
    }

    @Before("logCut()")
    public void before(JoinPoint joinPoint) {
        Map<String, Object> stackValue = RequestThreadLocal.getStack();
        if (stackValue != null) {
            stackValue.put(costKey(joinPoint), System.currentTimeMillis());
        } else {
            stackValue = new HashMap<>();
            stackValue.put(costKey(joinPoint), System.currentTimeMillis());
            RequestThreadLocal.setRequestStack(stackValue);
        }
        Log log = getAnnotation(joinPoint.getTarget().getClass(), Log.class);
        LogAdvice.log.info("==={} request start=== 【desc】: {} 【url】: {} 【param】: {} 【headers】:{} ",
                type(joinPoint), desc(joinPoint), url(joinPoint), printObj(log.encrypt(), joinPoint.getArgs()), LoginUtil.getHeaders());
    }

    /**
     * 判断当前请求是app请求还是feign请求
     */
    private String type(JoinPoint pjp) {
        Class<?> tClass = pjp.getTarget().getClass();
        if (isFeignClass(tClass)) {
            Target.HardCodedTarget<?> target = getFeignTarget(pjp);
            return target != null ? target.name() + " Feign" : "Feign";
        } else if (isController(tClass)) {
            return "APP";
        }
        return "??";
    }

    /**
     * todo 接口性能分析,max,min,avg,99line,95line,QPS
     * methodName set<time>,
     */
    @AfterReturning(pointcut = "logCut()", returning = "result")
    public void after(JoinPoint joinPoint, Object result) {
        Log log = getAnnotation(joinPoint.getTarget().getClass(), Log.class);
        LogAdvice.log.info("==={} request success=== 【desc】: {} 【url】: {} 【cost】: {}ms  【resp】: {}",
                type(joinPoint), desc(joinPoint), url(joinPoint), cost(joinPoint), printObj(log.encrypt(), result));
    }

    @AfterThrowing(pointcut = "logCut()", throwing = "t")
    public void exception(JoinPoint joinPoint, Throwable t) {
        if (t instanceof NormalBreakException) {
            Log log = getAnnotation(joinPoint.getTarget().getClass(), Log.class);
            LogAdvice.log.info("==={} request success=== 【desc】: {} 【url】: {} 【cost】: {}ms  【resp】: {}",
                    type(joinPoint), desc(joinPoint), url(joinPoint), cost(joinPoint), printObj(log.encrypt(), ((NormalBreakException) t).getDefaultData()));
            return;
        }
        recordException(t, "LogAdviceError, msg:{} 【url】: {}  【cost】: {}ms  【req】: {} 【headers】:{} ",
                t.toString(), url(joinPoint), cost(joinPoint), joinPoint.getArgs(), LoginUtil.getHeaders());
    }

    private String costKey(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        return START_TIME + method.toString();
    }

    private long cost(JoinPoint joinPoint) {
        Map<String, Object> stackValue = RequestThreadLocal.getStack();
        long startTime = (long) stackValue.get(costKey(joinPoint));
        return System.currentTimeMillis() - startTime;
    }

    private String desc(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        PostMapping annotation = method.getAnnotation(PostMapping.class);
        return annotation != null ? annotation.name() : "";
    }

    private String url(JoinPoint pjp) {
        Class<?> tClass = pjp.getTarget().getClass();
        if (isFeignClass(tClass)) {
            return getFeignUrlPre(pjp) + getUrlSuf(pjp);
        } else if (isController(tClass)) {
            return HttpRequestUtils.getRequestUrl();
        } else {
            recordException("not feign or controller request, actual:" + tClass);
            return "not feign or controller request ";
        }
    }

    private String getFeignUrlPre(JoinPoint pjp) {
        Target.HardCodedTarget<?> target = getFeignTarget(pjp);
        return target == null ? "" : target.url() + target.type().getAnnotation(FeignClient.class).path();
    }

    private static String getUrlSuf(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        return Optional.ofNullable(method.getAnnotation(PostMapping.class)).map(PostMapping::value)
                .orElseGet(() -> Optional.ofNullable(method.getAnnotation(GetMapping.class)).map(GetMapping::value)
                        .orElse(ERROR_SUFFIX_URL))[0];
    }

    private Target.HardCodedTarget<?> getFeignTarget(JoinPoint pjp) {
        Proxy target = (Proxy) pjp.getTarget();
        if (target.toString().contains(FEIGN_TARGET_CLASS_NAME)) {
            InvocationHandler feignInvocationHandler = Proxy.getInvocationHandler(target);
            return (Target.HardCodedTarget<?>) ReflectUtil.getFieldValue(feignInvocationHandler, FIELD_TARGET);
        } else {
            recordException("have @FeignClient but target is not HardCodedTarget, actual:" + target.toString());
            return null;
        }
    }

    private Object printObj(boolean encrypt, Object obj) {
        return isPrd && encrypt ? encrypt(obj) : obj;
    }

    private Object encrypt(Object obj) {
        return TryCatchUtil.ignoreError(() -> Base64Utils.encode64(obj), obj);
    }

}
相关文章
|
机器学习/深度学习 人工智能 API
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:1~5
TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:1~5
558 0
|
Java 程序员 API
《Spring Boot应用进阶:打造优雅的错误处理机制与全局异常拦截器》
《Spring Boot应用进阶:打造优雅的错误处理机制与全局异常拦截器》
404 0
|
JSON Java 数据格式
Java jackson 由String转成List和各种对象
时间久了,会忘记具体怎么转,记录一下,后面方便使用
1336 1
Quartz-CronTrigger解读
Quartz-CronTrigger解读
409 0
|
Java Maven
使用idea将普通项目转换为maven项目
使用idea将普通项目转换为maven项目
使用idea将普通项目转换为maven项目
|
前端开发 开发工具 git
React 16 Jest手动模拟(Manual Mocks)
转载地址 React 16 Jest手动模拟(Manual Mocks) 项目初始化 git clone https://github.com/durban89/webpack4-react16-reactrouter-demo.git  cd webpack4-react16-reactrouter-demo git fetch origin git checkout v_1.0.27 npm install   手动模拟(Manual Mocks) 手动模拟主要功能是用于存储模拟的数据。
1401 0
|
机器学习/深度学习
|
2天前
|
数据采集 人工智能 安全