本文目录导读:

AOP(面向切面编程)记录日志最常用的方式就是利用环绕通知,下面是几种常见场景的写法,从简单到复杂。
基础写法:记录方法入参和返回值
@Aspect
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
// 定义切面:所有Controller类下的所有方法
@Pointcut("execution(* com.example.controller.*.*(..))")
public void controllerLog() {}
@Around("controllerLog()")
public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
// 方法开始时间
long startTime = System.currentTimeMillis();
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getDeclaringTypeName() + "." + signature.getName();
// 获取参数
Object[] args = joinPoint.getArgs();
log.info("【请求】方法: {},参数: {}", methodName, args);
Object result = null;
try {
result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("【响应】方法: {},耗时: {}ms,结果: {}", methodName, endTime - startTime, result);
} catch (Exception e) {
log.error("【异常】方法: {},耗时: {}ms,异常: {}", methodName, System.currentTimeMillis() - startTime, e.getMessage());
throw e;
}
return result;
}
}
带自定义注解的写法(推荐)
1 创建日志注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperLog {
String value() default ""; // 操作描述
String type() default ""; // 操作类型
}
2 定义切面
@Aspect
@Component
public class OperLogAspect {
private static final Logger log = LoggerFactory.getLogger(OperLogAspect.class);
// 拦截所有带 @OperLog 注解的方法
@Pointcut("@annotation(com.example.annotation.OperLog)")
public void operLogPointcut() {}
@Around("operLogPointcut()")
public Object aroundOperLog(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 获取注解信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
OperLog operLog = method.getAnnotation(OperLog.class);
// 构建日志信息
String operDescription = operLog.value();
String operType = operLog.type();
String className = signature.getDeclaringTypeName();
String methodName = signature.getName();
// 获取参数
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args);
// 获取请求信息(Web环境)
HttpServletRequest request = getRequest();
String ip = getIpAddress(request);
String url = request.getRequestURI();
log.info("操作日志 - 描述: {}, 类型: {}, 类名: {}, 方法名: {}, IP: {}, URL: {}, 参数: {}",
operDescription, operType, className, methodName, ip, url, params);
try {
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("操作日志 - 耗时: {}ms, 结果: {}", endTime - startTime, JSON.toJSONString(result));
return result;
} catch (Exception e) {
log.error("操作日志 - 异常: {}", e.getMessage());
throw e;
}
}
private HttpServletRequest getRequest() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return attributes != null ? attributes.getRequest() : null;
}
private String getIpAddress(HttpServletRequest request) {
if (request == null) return "unknown";
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
3 使用注解
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/add")
@OperLog(value = "添加用户", type = "INSERT")
public Result addUser(@RequestBody User user) {
// 业务逻辑
return Result.success();
}
@PutMapping("/update")
@OperLog(value = "修改用户", type = "UPDATE")
public Result updateUser(@RequestBody User user) {
// 业务逻辑
return Result.success();
}
@DeleteMapping("/delete/{id}")
@OperLog(value = "删除用户", type = "DELETE")
public Result deleteUser(@PathVariable Long id) {
// 业务逻辑
return Result.success();
}
}
异步记录日志(推荐)
@Aspect
@Component
public class AsyncLogAspect {
@Around("@annotation(com.example.annotation.OperLog)")
@Async // 异步执行
public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
// ... 日志记录逻辑
// 注意:异步环境下 RequestContextHolder 可能获取不到,需要手动传递
return joinPoint.proceed();
}
}
完整的最佳实践示例
@Aspect
@Component
@Order(1)
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
// 定义切面
@Pointcut("execution(* com.example.controller.*.*(..))")
public void logPointcut() {}
@Around("logPointcut()")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
// 性能监控
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 构建日志信息
LogInfo logInfo = buildLogInfo(joinPoint);
try {
Object result = joinPoint.proceed();
stopWatch.stop();
logInfo.setResult(JSON.toJSONString(result));
logInfo.setCostTime(stopWatch.getTotalTimeMillis());
logInfo.setSuccess(true);
// 记录正常日志
log.info("接口日志: {}", logInfo);
return result;
} catch (Exception e) {
stopWatch.stop();
logInfo.setCostTime(stopWatch.getTotalTimeMillis());
logInfo.setSuccess(false);
logInfo.setErrorMsg(e.getMessage());
// 记录异常日志
log.error("接口异常: {}", logInfo, e);
throw e;
}
}
private LogInfo buildLogInfo(ProceedingJoinPoint joinPoint) {
LogInfo logInfo = new LogInfo();
// 获取方法信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取注解
OperLog operLog = method.getAnnotation(OperLog.class);
if (operLog != null) {
logInfo.setDescription(operLog.value());
logInfo.setType(operLog.type());
}
// 获取请求信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
logInfo.setIp(getIpAddress(request));
logInfo.setUrl(request.getRequestURI());
logInfo.setMethod(request.getMethod());
}
// 获取类和方法名
logInfo.setClassName(signature.getDeclaringTypeName());
logInfo.setMethodName(signature.getName());
// 获取参数
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
// 过滤掉 HttpServletRequest、HttpServletResponse 等
List<Object> filteredArgs = Arrays.stream(args)
.filter(arg -> !(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse))
.collect(Collectors.toList());
logInfo.setParams(JSON.toJSONString(filteredArgs));
}
return logInfo;
}
@Data
public static class LogInfo {
private String description;
private String type;
private String className;
private String methodName;
private String ip;
private String url;
private String httpMethod;
private String params;
private String result;
private long costTime;
private boolean success;
private String errorMsg;
}
}
注意事项
- 性能考虑:生产环境建议异步处理,避免日志记录影响业务性能
- 敏感信息:对密码等敏感信息进行脱敏处理
- 参数过滤:排除 HttpServletRequest、HttpServletResponse 等大对象
- 异常处理:确保日志记录不影响业务正常执行
- 循环依赖:注意切面类不要被自己的切面拦截
这样,你就有了一个完整、可扩展的AOP日志记录方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播