Java注解本身不执行逻辑,需配合AOP实现自动日志记录:定义@MethodTimer和@AuditLog两个职责分离的自定义注解,编写@Around切面统一处理耗时统计与审计日志,集成MDC、SecurityContext等增强可追溯性,并注意代理限制、异步写入等避坑点。

Java 注解本身不执行逻辑,但可以配合 AOP(面向切面编程)在方法执行前后自动织入日志记录逻辑。要实现「自动记录方法执行耗时」和「审计日志」,核心是:定义自定义注解 + 编写切面(@Aspect)+ 在切点中提取参数、计算耗时、构造日志内容。
定义耗时与审计两个自定义注解
建议分开定义,职责清晰,也便于单独启用或组合使用:
- @MethodTimer:仅标记需统计耗时的方法,可选是否输出到日志(如 DEBUG 级别);
- @AuditLog:标记需审计的方法,支持配置业务操作类型(如 "CREATE_USER")、是否记录参数/返回值、是否脱敏等。
示例(简洁版):
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodTimer {
String value() default ""; // 日志前缀,如 "UserService#login"
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuditLog {
String operation(); // 必填,如 "USER_LOGIN"
boolean includeArgs() default false;
boolean includeResult() default false;
boolean maskArgs() default true; // 敏感参数是否脱敏
}
编写切面统一处理逻辑
使用 Spring AOP(需引入 spring-boot-starter-aop),一个切面类即可覆盖两种注解:
立即学习“Java免费学习笔记(深入)”;
- 用 @Around 拦截带
@MethodTimer或@AuditLog的方法; - 通过
joinPoint.getSignature()获取方法签名,joinPoint.getArgs()获取参数; - 用
System.nanoTime()计算精确耗时(毫微秒级,转为毫秒更实用); - 结合 SLF4J(如 Logback)记录结构化日志,例如用
log.info("TIMER [{}] cost {}ms", method, costMs)和log.info("AUDIT [{}] op={} args={} result={}", ...); - 对敏感字段(如 password、idCard)做简单脱敏(如
StringUtils.substring(str, 0, 2) + "***"),或集成 Jackson 的@JsonIgnore/ 自定义序列化器。
增强日志可读性与可追溯性
光记耗时和参数不够,生产环境需要上下文支撑排查:
- 在日志中加入唯一 traceId(如通过 MDC 集成 Sleuth 或自定义 UUID),让一次请求的所有日志可串联;
- 审计日志中记录操作人(从 SecurityContext 获取当前 Subject)、客户端 IP(从 RequestContextHolder)、时间戳(ISO 格式);
- 对异常情况单独记录 ERROR 级别审计日志(如权限拒绝、参数校验失败),并在切面中捕获
throwing; - 避免在切面中记录大对象(如整个 Entity 列表),可只记 size、class、关键 ID 字段。
注意事项与避坑点
实际落地时容易忽略的关键细节:
- 切面默认不代理 private / final / static 方法,注解必须打在 public 实例方法上;
- 同一类内方法调用(A → B)不会触发切面,因为未经过代理对象 —— 需改为注入自身 Bean 调用,或改用 AspectJ 编译期织入;
- 避免在切面中执行耗时操作(如远程调用、DB 写入),否则会拖慢主流程;审计日志建议异步(如用 Logback 的 AsyncAppender);
- 注解属性尽量用字符串字面量,避免运行时反射取常量类,影响启动性能;
- 测试时记得开启 AOP:确保
@EnableAspectJAutoProxy(Spring Boot 默认已开)且切面类被 Spring 扫描到(加@Component)。


















