Spring AOP 中记录日志

Spring AOP 中记录日志

使用 AOP 和 Spring 提供的 RequestContextHolder 在通知中记录 HTTP 请求相关日志。以下是进阶添加日志功能的完整例子和说明。


完整示例

1. 切面类实现

java 复制代码
@Aspect
@Component
public class LogAspect {

    @Around("@annotation(log)") // 拦截所有标注 @Log 的方法
    public Object logExecution(ProceedingJoinPoint joinPoint, Log log) throws Throwable {
        // 获取 HttpServletRequest 对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 提取请求信息
        String url = request.getRequestURL().toString();
        String method = request.getMethod();
        String ip = request.getRemoteAddr();
        String params = request.getQueryString();

        // 方法名和参数
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();

        // 记录日志 - 方法执行前
        System.out.println("请求 URL: " + url);
        System.out.println("HTTP 方法: " + method);
        System.out.println("请求 IP: " + ip);
        System.out.println("请求参数: " + params);
        System.out.println("方法名称: " + methodName);
        System.out.println("方法参数: " + Arrays.toString(args));

        long startTime = System.currentTimeMillis();

        // 执行目标方法
        Object result;
        try {
            result = joinPoint.proceed(); // 执行被拦截的方法
        } catch (Throwable ex) {
            // 异常处理
            System.err.println("方法执行异常: " + methodName + ", 异常信息: " + ex.getMessage());
            throw ex;
        }

        long timeTaken = System.currentTimeMillis() - startTime;

        // 记录日志 - 方法执行后
        System.out.println("方法执行完成: " + methodName + ", 返回值: " + result + ", 耗时: " + timeTaken + "ms");

        return result; // 返回目标方法的执行结果
    }
}

2. 自定义注解

在需要记录日志的方法上标注自定义注解 @Log:

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

3. 控制器示例

在控制器方法上使用 @Log 注解:

java 复制代码
@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/test")
    @Log("测试日志记录")
    public String testLog(@RequestParam String input) {
        if ("error".equals(input)) {
            throw new RuntimeException("模拟异常");
        }
        return "Hello, " + input;
    }
}

日志输出示例

正常请求

访问 http://localhost:8080/api/test?input=world ,记录如下:

复制代码
请求 URL: http://localhost:8080/api/test
HTTP 方法: GET
请求 IP: 127.0.0.1
请求参数: input=world
方法名称: testLog
方法参数: [world]
方法执行完成: testLog, 返回值: Hello, world, 耗时: 10ms

异常请求

访问 http://localhost:8080/api/test?input=error ,记录如下:

复制代码
请求 URL: http://localhost:8080/api/test
HTTP 方法: GET
请求 IP: 127.0.0.1
请求参数: input=error
方法名称: testLog
方法参数: [error]
方法执行异常: testLog, 异常信息: 模拟异常

关键点解析

1. 为什么使用 RequestContextHolder?

  • HttpServletRequest 是与线程绑定的,通过 RequestContextHolder 可以方便地在 AOP 切面中获取当前请求的 HttpServletRequest 对象。

2. 日志内容可记录什么?

  • 请求的 URL (request.getRequestURL());
  • HTTP 方法 (request.getMethod());
  • 客户端 IP 地址 (request.getRemoteAddr());
  • 请求参数 (request.getQueryString());
  • 被拦截方法的名称和参数 (joinPoint.getSignature() 和 joinPoint.getArgs());
  • 方法执行耗时。

3. 异常处理

  • 在 catch 块中记录方法执行时报出的异常信息,以便后续排查问题。

总结

  • 通过 AOP 和 RequestContextHolder ,在通知中进行统一的日志记录,便于跟踪和排查 HTTP 请求相关的信息。
  • @Around 通知允许在方法执行前后和异常情况下插入日志逻辑,适用于统一的日志记录场景。
相关推荐
攻城狮Soar几秒前
详解volatile
开发语言·c++·1024程序员节
小羊没烦恼!几秒前
.NET无侵入自动化探针原理和主流实现
运维·开发语言·javascript·python·自动化·.net·1024程序员节
文丑颜不良啊1 分钟前
LeetCode701~725题目解析
java·leetcode
江屿风3 分钟前
【Linux-System】【System V共享内存:从原理到实战的完整解析】流食般投喂
linux·运维·服务器·笔记·系统架构·1024程序员节
東隅已逝,桑榆非晚6 分钟前
搜索二叉树
c++·笔记·学习·1024程序员节
ch.ju11 分钟前
Java——idea知识:Collection父接口
java·开发语言·intellij-idea
木井巳1 小时前
【动态规划: 线性dp】解码方法
java·算法·leetcode·动态规划·推荐算法
昵称画8 小时前
交接班记录电子化:三班之间的信息不漏
开发语言·数据库·人工智能·低代码·php
“AI国潮设计-小江”9 小时前
【AIGC实战】Python+SDXL批量生成“潮汕英歌舞不倒翁”IP | 角色一致性与多场景融合方案,附完整代码
开发语言·人工智能·python·prompt·aigc
沙蒿同学9 小时前
不引 Ghostscript:我用纯 Go 给 PDF 做了一场「压缩手术」
前端·后端·github