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
通知允许在方法执行前后和异常情况下插入日志逻辑,适用于统一的日志记录场景。