请求日志切面类

发布于:2025-06-16 ⋅ 阅读:(18) ⋅ 点赞:(0)
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 + ")";
            }
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到