aop打印请求日志

该代码示例展示了如何使用SpringAOP进行接口日志打印,特别是如何在切点参数中过滤请求和响应对象以避免序列化异常。它定义了一个切点在@RestController注解的类中,然后在方法调用前后记录时间戳,处理请求和响应参数,并利用Hutool库的JSONUtil将参数转换为JSON字符串进行日志输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

point  切点参数中如果 包含请求或者响应对象, 不能进行序列化, 以下代码做了过滤

package com.deloitte.crm.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 打印系统日志
 *
 * @author -_-
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class ReqPrintAop {

    private static final String REQ_LOG =
            "\n" +
            "╔══════════════════════════════════════════════════════════════════════════════════════════════\n" +
            "  请求耗时: {}ms\n" +
            "  请求方式: {}\n" +
            "  请求地址: {}\n" +
            "  方法路径: {}\n" +
            "  请求参数: {}\n" +
            "  响应数据: {}\n" +
            "╚══════════════════════════════════════════════════════════════════════════════════════════════";

    private final HttpServletRequest request;
    private static final ThreadLocal<Long> apiThreadLocal = new ThreadLocal<>();

    /**
     * 打印接口日志
     */
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void api() {
    }

    @Before(value = "api()")
    public void beforeApi() {
        apiThreadLocal.set(System.currentTimeMillis());
    }


    /**
     * 返回前通知
     *
     * @param point 切点
     * @param res   返回体
     */
    @AfterReturning(returning = "res", pointcut = "api()")
    public void doAfterReturning(JoinPoint point, Object res) {
        try {
            long reqTime = Long.MAX_VALUE;
            if (apiThreadLocal.get() != null) {
                reqTime = System.currentTimeMillis() - apiThreadLocal.get();
                apiThreadLocal.remove();
            }

            String methodWay = StrUtil.format("{}.{}",
                    point.getSignature().getDeclaringTypeName(),
                    point.getSignature().getName());
            ;
            String url = String.valueOf(request.getRequestURL());
            String method = request.getMethod();

            //过滤掉 请求和响应对象, 否则序列化会出现异常
            List<Object> reqParamList = Arrays.stream(point.getArgs())
                    .filter(x -> !(x instanceof ServletRequest || x instanceof ServletResponse))
                    .collect(Collectors.toList());

            String reqParams = JSONUtil.toJsonStr(reqParamList);
            String rspParams = JSONUtil.toJsonStr(res);
            log.debug(REQ_LOG, reqTime, method, url, methodWay, reqParams, rspParams);
        } catch (Exception e) {
            log.error("打印接口日志错误!", e);
        }
    }
}

### 如何使用 Java AOP 实现日志打印 #### 1. 配置 Spring AOP 环境 为了在 Java 项目中使用 AOP 进行日志记录,首先需要引入必要的依赖项。以下是 Maven 的 `pom.xml` 文件中的相关依赖: ```xml <dependencies> <!-- Spring Core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.10</version> </dependency> <!-- Spring Context (for @Aspect support) --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.10</version> </dependency> <!-- AspectJ Weaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> </dependency> </dependencies> ``` 以上配置确保了项目的环境能够支持 AOP 功能[^4]。 --- #### 2. 创建切面类 定义一个切面类用于拦截目标方法并记录日志。以下是一个典型的切面类示例: ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); @Around("execution(* com.example.service..*(..)))") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { return joinPoint.proceed(); // 执行被拦截的方法 } finally { long executionTime = System.currentTimeMillis() - start; String methodName = joinPoint.getSignature().toShortString(); logger.info("{} executed in {} ms", methodName, executionTime); } } } ``` 上述代码实现了以下几个功能: - 使用 `@Aspect` 注解标记该类为切面类。 - 使用 `@Around` 注解定义环绕通知,在方法执行前后进行处理。 - 记录方法名称及其耗时信息到日志文件中[^1]。 --- #### 3. 定义切入点表达式 切入点表达式的灵活性使得开发者可以精确控制哪些方法会被拦截。例如: - **匹配特定包下的所有方法** ```java @Around("execution(* com.example.service..*(..))") ``` - **匹配指定方法名** ```java @Around("execution(* com.example.service.MyService.myMethod(..))") ``` - **匹配特定参数类型的函数** ```java @Around("execution(* com.example.service.*.*(String, ..))") ``` 这些表达式可以根据需求自由组合,满足不同的场景需求[^3]。 --- #### 4. 测试 AOP 日志功能 创建一个简单的控制器或服务层方法来测试日志记录效果。例如: ```java @RestController @RequestMapping("/api/test") public class TestController { @GetMapping("/hello") public String sayHello() { return "Hello, World!"; } @PostMapping("/greet") public String greet(@RequestBody Map<String, String> body) { return "Greeting: " + body.get("name"); } } ``` 当调用 `/api/test/hello` 或 `/api/test/greet` 接口时,AOP 切面会自动捕获请求并记录相关信息。 --- #### 5. 输出样例 假设客户端发送了一个 POST 请求至 `/api/test/greet` 并传递 `{ "name": "John" }` 参数,则控制台可能输出如下内容: ``` INFO LoggingAspect - com.example.controller.TestController.greet executed in 12 ms ``` 这表明 AOP 成功拦截了方法调用,并记录了其运行时间和其他元数据[^2]。 --- ### 总结 通过 Spring AOP 可以轻松实现日志记录功能,不仅提高了代码的模块化程度,还减少了重复劳动。借助强大的切点表达式语法,开发者可以灵活地选择要监控的目标方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值