《 接口日志与异常处理统一设计:AOP与全局异常捕获》
🌟 接口日志与异常处理统一设计:AOP与全局异常捕获
在分布式系统复杂度日益增长的今天,混乱的日志与零散的异常处理已成为系统维护的噩梦。本文将深入解析如何通过AOP切面与全局异常捕获构建统一的日志异常处理体系,助力打造高可维护、易追踪的生产级系统。
文章目录
- 🌟 接口日志与异常处理统一设计:AOP与全局异常捕获
- 🔍 一、背景:为什么需要统一处理?
- 💡 分布式系统痛点
- 📌 统一处理的优势
- ⚙️ 二、AOP切面:统一日志增强
- 💡 切面设计原理
- 🔧 日志切面核心实现
- 🔍 MDC配置示例(logback.xml)
- 📝 日志输出样例
- 🛡 三、全局异常处理
- 💡 异常处理架构
- 🔧 统一异常处理器
- 🧩 异常体系设计
- 📦 四、统一响应结构
- 💡 响应体设计
- 📄 JSON响应示例
- 🚀 五、实战:统一处理体系落地
- 💡 全链路追踪集成
- 🔔 异常报警机制
- 📦 Starter封装示例
- 💎 六、总结与最佳实践
- 🏆 核心价值
- 📌 日志规范最佳实践
- 1.必备字段:
- 2.安全规范:
- 3.异常处理原则:
- 🚨 避坑指南
- 1.不要捕获所有异常:
- 2.避免过度日志:
- 3.TraceID传递规范:
🔍 一、背景:为什么需要统一处理?
💡 分布式系统痛点
📌 统一处理的优势
1.全链路追踪:TraceID贯穿请求生命周期
2.标准化日志:统一格式便于ELK采集分析
3.异常治理:规范化错误处理流程
4.监控报警:快速定位问题根源
5.安全合规:敏感信息脱敏处理
案例分享:某电商平台在统一日志异常体系后,故障定位时间从平均4小时缩短至15分钟
⚙️ 二、AOP切面:统一日志增强
💡 切面设计原理
🔧 日志切面核心实现
@Aspect
@Component
@Slf4j
public class LogAspect {// 切入所有Controller方法@Around("execution(* com.example.controller..*(..))")public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {// 1. 生成唯一TraceIDString traceId = UUID.randomUUID().toString().replace("-", "");MDC.put("traceId", traceId);// 2. 获取请求信息ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 3. 记录请求日志log.info("Request: URL={}, Method={}, Args={}",request.getRequestURL(),request.getMethod(),Arrays.toString(joinPoint.getArgs()));long start = System.currentTimeMillis();Object result = null;try {// 4. 执行目标方法result = joinPoint.proceed();} finally {// 5. 记录响应日志long time = System.currentTimeMillis() - start;log.info("Response: Time={}ms, Result={}", time, result);MDC.clear(); // 清除上下文}return result;}
}
🔍 MDC配置示例(logback.xml)
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%X{traceId}] [%thread] [%-5level] [%logger{36}] - %msg%n</pattern></encoder>
</appender>
📝 日志输出样例
[2023-10-01 14:30:25.123] [d72fe182f87c4a0cb88259] [http-nio-8080-exec-1] [INFO] [c.e.a.LogAspect]
Request: URL=http://localhost:8080/user/123, Method=GET, Args=[123][2023-10-01 14:30:25.456] [d72fe182f87c4a0cb88259] [http-nio-8080-exec-1] [INFO] [c.e.a.LogAspect]
Response: Time=333ms, Result=User(id=123, name=张三)
🛡 三、全局异常处理
💡 异常处理架构
🔧 统一异常处理器
@ControllerAdvice
public class GlobalExceptionHandler {// 处理业务异常@ExceptionHandler(BusinessException.class)@ResponseBodypublic CommonResult<Void> handleBusinessException(BusinessException e) {log.error("业务异常: code={}, msg={}", e.getCode(), e.getMessage());return CommonResult.fail(e.getCode(), e.getMessage());}// 处理系统异常@ExceptionHandler(Exception.class)@ResponseBodypublic CommonResult<Void> handleException(Exception e) {log.error("系统异常: ", e);return CommonResult.fail(ErrorCode.SYSTEM_ERROR);}
}
🧩 异常体系设计
// 错误码枚举
public enum ErrorCode {SUCCESS("00000", "成功"),USER_NOT_FOUND("A0001", "用户不存在"),SYSTEM_ERROR("B0001", "系统繁忙");private final String code;private final String msg;
}// 业务异常基类
public class BusinessException extends RuntimeException {private final String code;public BusinessException(ErrorCode errorCode) {super(errorCode.getMsg());this.code = errorCode.getCode();}
}// 自定义业务异常
public class UserNotFoundException extends BusinessException {public UserNotFoundException() {super(ErrorCode.USER_NOT_FOUND);}
}
📦 四、统一响应结构
💡 响应体设计
@Data
public class CommonResult<T> {private String code;private String message;private T data;private String traceId = MDC.get("traceId");public static <T> CommonResult<T> success(T data) {CommonResult<T> result = new CommonResult<>();result.setCode(ErrorCode.SUCCESS.getCode());result.setMessage(ErrorCode.SUCCESS.getMsg());result.setData(data);return result;}public static CommonResult<Void> fail(String code, String message) {CommonResult<Void> result = new CommonResult<>();result.setCode(code);result.setMessage(message);return result;}
}
📄 JSON响应示例
// 成功响应
{"code": "00000","message": "成功","data": {"userId": 123,"username": "张三"},"traceId": "d72fe182f87c4a0cb88259"
}// 失败响应
{"code": "A0001","message": "用户不存在","data": null,"traceId": "d72fe182f87c4a0cb88259"
}
🚀 五、实战:统一处理体系落地
💡 全链路追踪集成
🔔 异常报警机制
@ExceptionHandler(Exception.class)
@ResponseBody
public CommonResult<Void> handleException(Exception e) {// 1. 记录异常日志log.error("系统异常: ", e);// 2. 发送钉钉报警DingTalkMessage message = new DingTalkMessage();message.setTitle("系统异常报警");message.setContent("异常信息: " + e.getMessage());message.setTraceId(MDC.get("traceId"));dingTalkService.send(message);return CommonResult.fail(ErrorCode.SYSTEM_ERROR);
}
📦 Starter封装示例
// 自动配置类
@Configuration
@Import({ LogAspect.class, GlobalExceptionHandler.class })
@ConditionalOnWebApplication
public class UnifiedLogAutoConfiguration {@Beanpublic CommonResult commonResult() {return new CommonResult();}
}// spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.unifiedlog.autoconfigure.UnifiedLogAutoConfiguration
💎 六、总结与最佳实践
🏆 核心价值
1.可追踪性:TraceID贯穿全链路
2.可维护性:标准化日志与异常
3.可观测性:集成监控报警体系
4.安全性:敏感数据脱敏处理
📌 日志规范最佳实践
1.必备字段:
- traceId: 全链路唯一ID
- userId: 当前操作用户
- clientIp: 客户端IP
- requestUrl: 请求路径
- method: HTTP方法
- costTime: 耗时(ms)
2.安全规范:
- 脱敏敏感数据(手机号、身份证)
- 禁止打印完整SQL
- 避免记录大文件内容
3.异常处理原则:
🚨 避坑指南
1.不要捕获所有异常:
// 错误做法:吞掉异常
try {// ...
} catch (Exception e) {// 无任何处理
}// 正确做法:有选择捕获
try {// ...
} catch (BusinessException e) {// 业务异常处理
} catch (Exception e) {// 记录日志并上报
}
2.避免过度日志:
- 生产环境关闭DEBUG日志
- 高频操作日志降级
- 批量操作只记录摘要
3.TraceID传递规范:
- 微服务间通过HTTP Header传递
- 异步线程池需手动传递
- MQ消息需携带TraceID
统一的日志异常体系是系统可维护性的基石。建议:
从项目初期就建立规范
强制TraceID全链路传递
定期审计异常日志
记住:好的日志系统不是记录所有信息,而是在需要时能快速找到关键信息