请求日志切面类

java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Stopwatch;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import lombok.Generated;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.server.ResponseStatusException;

/**
 * 日志切面类,用于记录Controller和RestController方法的执行情况
 */
@EnableAspectJAutoProxy
@Aspect
@Component
public class LogAspect {
    // 日志记录器
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
    
    // JSON序列化工具
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    
    // 需要忽略的参数类型列表
    private static final List<Class<?>> IGNORED_CLASSES = List.of(
        HttpServletRequest.class, 
        HttpServletResponse.class, 
        HttpSession.class, 
        Model.class
    );
    
    // 空参数默认值
    private static final String EMPTY_PARAMS = "empty";

    // 静态初始化块,配置ObjectMapper
    static {
        // 禁用日期时间戳格式
        OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        
        // 注册Java时间模块
        JavaTimeModule module = new JavaTimeModule();
        // 使用Spring的日期时间格式化
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
        OBJECT_MAPPER.registerModule(module);
    }

    @Pointcut("within(@org.springframework.stereotype.Controller *) || " +
              "within(@org.springframework.web.bind.annotation.RestController *)")
    public void logPointcut() {}

    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        
        // 使用Spring的ResponseStatus注解替代IgnoreLog
        if (Objects.nonNull(AnnotationUtils.findAnnotation(signature.getMethod(), ResponseStatus.class))) {
            return point.proceed(point.getArgs());
        }

        String baseLog = "empty";
        Stopwatch sw = Stopwatch.createStarted();

        try {
            baseLog = OBJECT_MAPPER.writeValueAsString(getLogBody(point));
            log.info("请求开始 ==> {}", baseLog);
        } catch (Exception e) {
            log.error("记录请求开始日志时出错", e);
        }

        Object proceedResult;
        try {
            proceedResult = point.proceed(point.getArgs());
        } catch (ResponseStatusException e) {  // 使用Spring的ResponseStatusException替代BizException
            log.error("业务异常 <== {}, 总耗时: {}, 错误信息: {}", baseLog, sw.stop(), e.getReason());
            throw e;
        } catch (Throwable e) {
            log.error("请求异常 <== {}, 总耗时: {}", baseLog, sw.stop(), e);
            throw e;
        }

        try {
            log.info("请求结束 <== {}, 总耗时: {}", baseLog, sw.stop());
        } catch (Exception e) {
            log.error("记录请求结束日志时出错", e);
        }

        return proceedResult;
    }

    private LogPrintBody getLogBody(ProceedingJoinPoint joinPoint) throws Throwable {
        LogPrintBody.LogPrintBodyBuilder builder = LogPrintBody.builder();
        
        Object[] args = joinPoint.getArgs();
        builder.params(ArrayUtils.isEmpty(args) ? EMPTY_PARAMS : Arrays.stream(args)
                .filter(arg -> Objects.isNull(arg) || !IGNORED_CLASSES.contains(arg.getClass()))
                .toList());

        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (sra != null) {
            HttpServletRequest request = sra.getRequest();
            builder.uri(request.getRequestURI())
                    .host(request.getRemoteHost())
                    .currentUserId(request.getHeader("currentLoginUserId"))
                    .fromApplication(request.getHeader("originAppName"))
                    .regular(request.getHeader("regular"))
                    .brand(request.getHeader("tag"));
        } else {
            builder.uri("未找到请求");
        }

        return builder.build();
    }

    @Generated
    public static class LogPrintBody {
        private final String uri;
        private final String currentUserId;
        private final String fromApplication;
        private final String host;
        private final String brand;
        private final String regular;
        private final Object params;

        private LogPrintBody(String uri, String currentUserId, String fromApplication, 
                           String host, String brand, String regular, Object params) {
            this.uri = uri;
            this.currentUserId = currentUserId;
            this.fromApplication = fromApplication;
            this.host = host;
            this.brand = brand;
            this.regular = regular;
            this.params = params;
        }

        public String uri() { return uri; }
        public String currentUserId() { return currentUserId; }
        public String fromApplication() { return fromApplication; }
        public String host() { return host; }
        public String brand() { return brand; }
        public String regular() { return regular; }
        public Object params() { return params; }

        @Generated
        public static class LogPrintBodyBuilder {
            private String uri;
            private String currentUserId;
            private String fromApplication;
            private String host;
            private String brand;
            private String regular;
            private Object params;

            public LogPrintBodyBuilder() {}

            public LogPrintBodyBuilder uri(String uri) {
                this.uri = uri;
                return this;
            }

            public LogPrintBodyBuilder currentUserId(String currentUserId) {
                this.currentUserId = currentUserId;
                return this;
            }

            public LogPrintBodyBuilder fromApplication(String fromApplication) {
                this.fromApplication = fromApplication;
                return this;
            }

            public LogPrintBodyBuilder host(String host) {
                this.host = host;
                return this;
            }

            public LogPrintBodyBuilder brand(String brand) {
                this.brand = brand;
                return this;
            }

            public LogPrintBodyBuilder regular(String regular) {
                this.regular = regular;
                return this;
            }

            public LogPrintBodyBuilder params(Object params) {
                this.params = params;
                return this;
            }

            public LogPrintBody build() {
                return new LogPrintBody(uri, currentUserId, fromApplication, host, brand, regular, params);
            }

            @Override
            public String toString() {
                return "LogPrintBodyBuilder(uri=" + uri + 
                       ", currentUserId=" + currentUserId + 
                       ", fromApplication=" + fromApplication + 
                       ", host=" + host + 
                       ", brand=" + brand + 
                       ", regular=" + regular + 
                       ", params=" + params + ")";
            }
        }
    }
}
相关推荐
2301_792674862 小时前
java学习(day34)
java·开发语言·学习
拾光Ծ2 小时前
【Linux系统】线程(上)
java·linux·运维·jvm·线程·c/c++
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第54题】【JVM篇】第14题:什么是可达性分析算法?
java·jvm·算法·面试
接着奏乐接着舞2 小时前
java jvm知识点
java·开发语言·jvm
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第55题】【JVM篇】第15题:JVM有哪些垃圾收集算法?
java·jvm·算法·面试
摇滚侠2 小时前
Java 基础面试题 真正的 offer 偏方 Java 基础 Java 高级
java·开发语言
蚰蜒螟3 小时前
深入剖析 OpenJDK 17 解释器中的安全点(Safepoint)进入与退出机制
java·开发语言·安全
Generalzy3 小时前
为什么 Go 的注释,能控制编译器?
java·python·golang
Galsk3 小时前
Linux零拷贝
java·linux·服务器·面试
不知名的忻3 小时前
归并排序(Java)
java·算法·排序算法