SpringBoot 全局异常处理与日志记录——统一规范实战

📅 2026/7/7 15:13:14 👁️ 阅读次数 📝 编程学习
SpringBoot 全局异常处理与日志记录——统一规范实战

线上系统出问题不可怕,可怕的是出了问题找不到原因。统一的异常处理和规范的日志记录是任何生产系统必备的基础设施。

一、全局异常处理

1. 自定义业务异常

publicclassBusinessExceptionextendsRuntimeException{privateintcode;privateStringmessage;publicBusinessException(intcode,Stringmessage){super(message);this.code=code;this.message=message;}// 常用错误码publicstaticBusinessExceptionnotFound(Stringmsg){returnnewBusinessException(404,msg);}publicstaticBusinessExceptionbadRequest(Stringmsg){returnnewBusinessException(400,msg);}publicstaticBusinessExceptionunauthorized(Stringmsg){returnnewBusinessException(401,msg);}publicstaticBusinessExceptionforbidden(Stringmsg){returnnewBusinessException(403,msg);}}

2. 全局异常处理器

@RestControllerAdvicepublicclassGlobalExceptionHandler{privatestaticfinalLoggerlog=LoggerFactory.getLogger(GlobalExceptionHandler.class);@ExceptionHandler(BusinessException.class)publicResultVO<?>handleBusiness(BusinessExceptione){log.warn("业务异常: code={}, msg={}",e.getCode(),e.getMessage());returnResultVO.error(e.getCode(),e.getMessage());}@ExceptionHandler(MethodArgumentNotValidException.class)publicResultVO<?>handleValidation(MethodArgumentNotValidExceptione){Stringmsg=e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining(";"));returnResultVO.error(400,msg);}@ExceptionHandler(ConstraintViolationException.class)publicResultVO<?>handleConstraint(ConstraintViolationExceptione){Stringmsg=e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";"));returnResultVO.error(400,msg);}@ExceptionHandler(NoHandlerFoundException.class)publicResultVO<?>handle404(NoHandlerFoundExceptione){returnResultVO.error(404,"接口不存在: "+e.getRequestURL());}@ExceptionHandler(Exception.class)publicResultVO<?>handleOther(Exceptione){log.error("系统异常",e);returnResultVO.error(500,"服务器繁忙,请稍后重试");}}

二、统一日志记录

1. 请求日志拦截器

@ComponentpublicclassLogInterceptorimplementsHandlerInterceptor{privatestaticfinalLoggerlog=LoggerFactory.getLogger(LogInterceptor.class);@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){// 记录请求开始时间request.setAttribute("startTime",System.currentTimeMillis());returntrue;}@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex){longstart=(long)request.getAttribute("startTime");longduration=System.currentTimeMillis()-start;log.info("[{}] {} {} → {} ({}ms)",request.getMethod(),request.getRequestURI(),request.getQueryString()!=null?"?"+request.getQueryString():"",response.getStatus(),duration);}}

2. 注册拦截器

@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@AutowiredprivateLogInterceptorlogInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(logInterceptor).addPathPatterns("/api/**");}}

3. 日志级别配置

logging:level:root:INFOcom.zhang:DEBUGcom.zhang.seckill.mapper:TRACEfile:path:/var/log/appname:${logging.file.path}/app.loglogback:rolling policy:max-history:30max-file-size:100MB

三、日志最佳实践

// ✅ 正确:带参数占位符log.info("用户 {} 创建订单 {}",userId,orderNo);// ❌ 错误:字符串拼接log.info("用户 "+userId+" 创建订单 "+orderNo);// ✅ 正确:包含上下文信息log.warn("支付失败: orderNo={}, amount={}, error={}",orderNo,amount,error);// ❌ 错误:没信息量的日志log.info("支付失败");

💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。