Spring Boot全局异常处理与日志监控实战指南
全局异常处理机制
1. @ControllerAdvice基础用法
使用@ControllerAdvice
注解可以创建全局异常处理器,它会拦截所有控制器抛出的异常。典型实现方式如下:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
ErrorResponse error = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"系统发生异常",
ex.getMessage(),
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
2. 自定义业务异常处理
针对特定业务异常创建自定义处理逻辑:
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
ErrorResponse error = new ErrorResponse(
ex.getErrorCode(),
ex.getErrorType(),
ex.getMessage(),
System.currentTimeMillis()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
3. 异常分类处理策略
按异常类型分类处理:
- 客户端异常(4xx):处理参数校验、资源不存在等异常
- 服务端异常(5xx):处理数据库、第三方服务等系统异常
- 业务异常:处理特定业务规则触发的异常
日志监控体系搭建
1. 日志配置最佳实践
application.yml
配置示例:
logging:
level:
root: INFO
org.springframework.web: DEBUG
com.example: TRACE
file:
name: logs/app.log
max-history: 30
max-size: 10MB
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"
2. 关键日志埋点位置
建议在以下位置添加详细日志:
- 请求入口:记录请求参数、请求头
- 业务处理:关键业务步骤和决策点
- 异常捕获:记录异常堆栈和上下文
- 外部调用:记录请求/响应数据
3. 日志监控方案选型
方案 | 特点 | 适用场景 |
---|---|---|
ELK Stack | 完整日志收集分析方案 | 中大型分布式系统 |
Splunk | 商业日志分析工具 | 企业级应用 |
Prometheus+Grafana | 指标监控+日志联动 | 云原生环境 |
Loki | 轻量级日志聚合 | 资源受限环境 |
实战案例分析
案例1:电商订单异常处理
@ExceptionHandler(OrderException.class)
public ResponseEntity<ErrorResponse> handleOrderException(OrderException ex) {
log.error("订单处理异常,订单号:{},用户:{}",
ex.getOrderId(), ex.getUserId(), ex);
ErrorResponse error = new ErrorResponse(
ex.getErrorCode(),
"订单处理失败",
ex.getMessage(),
System.currentTimeMillis()
);
// 发送告警通知
alertService.sendOrderErrorAlert(ex);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
案例2:微服务链路日志追踪
使用MDC实现请求链路追踪:
@Slf4j
@Aspect
@Component
public class RequestLogAspect {
@Around("execution(* com.example..*Controller.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
log.info("请求开始: {} {}",
joinPoint.getSignature().getDeclaringTypeName(),
joinPoint.getSignature().getName());
try {
Object result = joinPoint.proceed();
log.info("请求成功: {}", result);
return result;
} catch (Exception ex) {
log.error("请求异常", ex);
throw ex;
} finally {
MDC.clear();
}
}
}
性能优化建议
异常处理性能优化:
-
避免在异常处理中执行耗时操作
- 异常处理流程应尽量轻量级,避免在catch块中执行数据库查询、远程调用等耗时操作
- 示例:将耗时的业务逻辑移出异常处理块,改为在正常流程中处理
- 典型场景:支付系统异常处理中不应包含订单状态更新等复杂操作
-
对高频异常进行缓存处理
- 对频繁出现的相同异常可建立缓存机制
- 实现方式:使用LRU缓存存储最近N次的异常信息
- 应用场景:接口限流异常、参数校验异常等高频异常
日志输出优化:
-
使用占位符{}代替字符串拼接
- 优势:避免不必要的字符串拼接开销
- 示例:log.info("用户{}登录成功", userId) 优于 log.info("用户"+userId+"登录成功")
- 支持框架:Log4j2、SLF4J等主流日志框架
-
合理设置日志级别
- 生产环境建议配置:
- 常规日志:INFO级别
- 调试日志:WARN级别
- 错误日志:ERROR级别
- 开发环境可开启DEBUG级别方便调试
- 生产环境建议配置:
-
敏感信息脱敏处理
- 必须脱敏的信息类型:
- 用户身份证号(保留前3位后4位)
- 银行卡号(保留前4位后4位)
- 手机号码(保留前3位后4位)
- 实现方式:自定义Logback/Log4j2的Converter
- 必须脱敏的信息类型:
监控系统调优:
-
日志采样率设置
- 高流量系统建议采用1%-10%的采样率
- 关键业务日志可适当提高采样率
- 实现方式:通过日志框架的Sampler组件配置
-
关键指标告警配置
- 必须监控的核心指标:
- 错误率(5分钟内>1%触发告警)
- 响应时间(P99>500ms触发告警)
- 系统负载(CPU>80%持续5分钟触发告警)
- 告警方式:企业微信/钉钉/SMS多通道通知
- 必须监控的核心指标:
-
日志数据清理
- 清理策略建议:
- 调试日志保留7天
- 业务日志保留30天
- 审计日志保留180天
- 实现方案:ELK的Curator工具或自定义清理脚本
- 存储优化:冷热数据分离存储
- 清理策略建议: