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 通知允许在方法执行前后和异常情况下插入日志逻辑,适用于统一的日志记录场景。
相关推荐
aqiu1111113 小时前
【C++算法打怪专栏】8. 字符串转换整数 (atoi)
开发语言·c++·算法
爱吃香菜的初学者3 小时前
十四.Linux——消息队列
linux·运维·开发语言
迅猛龙办公室4 小时前
python实现倒序输出数字
开发语言·python
夏幻灵10 小时前
JavaScript this 面试:五种绑定规则、优先级与常见面试陷阱
开发语言·javascript·面试
程序猿编码10 小时前
榨干 RTX5090 算力!Qwen3 专用单卡推理引擎,手写C++/CUDA 算子实现 MTP 推测解码
开发语言·c++·大模型推理·qwen3·多模态推理
苍何10 小时前
开源微信流 Windows,微信聊天记录,可以直接给 Codex 和 Obsidan 了
后端
bkspiderx11 小时前
Qt 插件机制:动态扩展应用功能的核心框架
开发语言·qt·元数据·qt 插件·qpluginloader
代码什么用11 小时前
Spring基础使用
java·后端·spring
程序员老陆11 小时前
深入理解 C++ thread_local:线程私有存储的正确打开方式
开发语言·c++·程序设计
沐晓时光12 小时前
C语言入门,深入理解指针(3)
c语言·开发语言