一、前言
在实际项目开发中,日志记录是非常重要的一个环节,它可以帮助我们追踪请求流程、排查问题、监控系统运行状态等。本文将详细介绍如何使用Spring AOP技术实现方法级别的日志记录功能。
二、核心组件介绍
1. 自定义注解 LogAnnotation
java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
}
- @Target : 指定注解可以应用的位置,包括方法(
METHOD)和类型(TYPE) - @Retention : 设置注解保留策略为运行时(
RUNTIME),确保在运行时可以通过反射获取注解信息 - 这个注解作为一个标记,用于标识需要进行日志记录的方法或类
2. AOP 切面类 LogAspectAop
java
@Aspect
@Component
public class LogAspectAop {
private final static Logger logger = LoggerFactory.getLogger(LogAspectAop.class);
- @Aspect: 标记这是一个切面类
- @Component: 将切面类注册为Spring容器中的Bean
- 使用SLF4J作为日志框架
三、核心实现分析
1. 切点定义
java
@Pointcut("@annotation(jerry.annotation.LogAnnotation)")
public void logAspectAop() {
}
- 定义切点表达式,匹配所有使用
@LogAnnotation注解的方法 - 当目标方法被
@LogAnnotation注解标记时,AOP会拦截并执行相应逻辑
2. 方法环绕处理
java
@Around("logAspectAop()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
String reqId = UUID.randomUUID().toString();
try {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
long startTime = System.currentTimeMillis();
logger.info("REQUEST_ID: {}, Request Info - URL: {}, HTTP_METHOD: {}, IP: {}, CLASS_METHOD: {}.{}, ARGS: {}, TIME: {}",
reqId,
request.getRequestURL().toString(),
request.getMethod(),
request.getRemoteAddr(),
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName(),
JSON.toJSONString(joinPoint.getArgs()),
MyDateUtil.formatDateTime(LocalDateTime.now()));
// 执行目标方法
Object result = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - startTime;
logger.info("REQUEST_ID: {}, RESPONSE : {}, EXECUTION_TIME: {}ms, TIME: {}",
reqId,
new ObjectMapper().writeValueAsString(result),
executionTime,
MyDateUtil.formatDateTime(LocalDateTime.now()));
return result;
} catch (Exception e) {
logger.error("REQUEST_ID: {}, ERROR: {}, TIME: {}", reqId, e.getMessage(), MyDateUtil.formatDateTime(LocalDateTime.now()), e);
throw e;
}
}
方法实现了以下功能:
- 统一请求追踪 :使用
UUID生成唯一的reqId来关联请求和响应日志 - 性能监控:记录接口执行时间,计算执行的时间
- 完整日志记录:记录请求信息(URL、方法、IP、参数等)和响应结果
- 异常处理:捕获并记录异常信息,同时将异常重新抛出
- 环绕通知 :使用
@Around注解实现方法执行前后的完整拦截
该方法解决了原来 @Before 和 @AfterReturning 分离导致的关联性问题,通过 reqId 实现了请求响应的精准匹配。
四、使用方法
在需要记录日志的方法上添加@LogAnnotation注解:
java
@GetMapping("/test")
@ApiOperation("测试日志打印")
@LogAnnotation
public String someMethod(String param) {
// 业务逻辑
return "result";
}
当调用该方法时,AOP会自动记录请求信息和响应结果。
五、设计优势
- 无侵入性: 通过注解方式实现,不影响原有业务代码
- 灵活配置: 可以选择性地对特定方法添加日志记录
- 统一管理: 集中处理日志记录逻辑,便于维护
- 信息全面: 记录了请求和响应的完整信息,便于问题排查
六、注意事项
- 性能考虑: 日志记录会带来一定的性能开销,对于高频调用的方法需谨慎使用
- 敏感信息: 注意过滤敏感参数信息,避免泄露用户隐私
- 异常处理 : 代码中使用了
catch(ignore)模式,确保日志异常不影响业务流程 - 依赖引入: 需要引入AOP、JSON处理等相关依赖包
七、总结
通过Spring AOP和自定义注解的方式,我们可以轻松实现方法级别的日志记录功能。这种方式既保持了代码的简洁性,又提供了强大的日志记录能力,是企业级应用开发中的常用实践方案。