SpringBoot 全局异常处理与日志记录——统一规范实战
线上系统出问题不可怕可怕的是出了问题找不到原因。统一的异常处理和规范的日志记录是任何生产系统必备的基础设施。一、全局异常处理1. 自定义业务异常publicclassBusinessExceptionextendsRuntimeException{privateintcode;privateStringmessage;publicBusinessException(intcode,Stringmessage){super(message);this.codecode;this.messagemessage;}// 常用错误码publicstaticBusinessExceptionnotFound(Stringmsg){returnnewBusinessException(404,msg);}publicstaticBusinessExceptionbadRequest(Stringmsg){returnnewBusinessException(400,msg);}publicstaticBusinessExceptionunauthorized(Stringmsg){returnnewBusinessException(401,msg);}publicstaticBusinessExceptionforbidden(Stringmsg){returnnewBusinessException(403,msg);}}2. 全局异常处理器RestControllerAdvicepublicclassGlobalExceptionHandler{privatestaticfinalLoggerlogLoggerFactory.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){Stringmsge.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining());returnResultVO.error(400,msg);}ExceptionHandler(ConstraintViolationException.class)publicResultVO?handleConstraint(ConstraintViolationExceptione){Stringmsge.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{privatestaticfinalLoggerlogLoggerFactory.getLogger(LogInterceptor.class);OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){// 记录请求开始时间request.setAttribute(startTime,System.currentTimeMillis());returntrue;}OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex){longstart(long)request.getAttribute(startTime);longdurationSystem.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/爬虫 实战干货不让你白来。