业务警告应封装为带业务码的runtimeexception并返回http 200 warning字段;系统严重错误仅限error子类,禁止捕获恢复,需jvm参数+p0告警;系统异常(exception)须分层兜底、熔断降级;日志级别必须与异常类型严格匹配。

关键在于把“业务警告”归为可预期、可响应的业务异常,把“系统严重错误”视为不可恢复、不应捕获的底层故障——不是靠日志级别区分,而是靠异常类型设计和处理策略分离。
业务警告:用自定义 RuntimeException 封装,不打断流程
业务警告本质是正常业务分支,比如“库存不足但可改选其他商品”“用户余额低于阈值但支付仍成功”。这类情况不该抛 Error,也不该用 checked exception 强制上层处理,而应:
- 继承 RuntimeException(如
UserBalanceWarningException),类名带 Warning 或 Hint,明确语义 - 在异常中携带业务码(如
WARN_BALANCE_LOW)、关键参数(currentBalance=9.5)、建议动作("建议充值") - 由全局异常处理器统一转为 HTTP 200 响应体中的 warning 字段,前端自主决定是否弹窗提示
- 日志打 WARN 级别,不触发 P0 告警,但接入监控看板做趋势分析(如每小时低余额警告超 100 次则发 Slack 提醒)
系统严重错误:只捕获 Throwable 中的 Error 子类,且仅记录+退出
OutOfMemoryError、StackOverflowError、NoClassDefFoundError 这些不是“错误”,而是 JVM 已失稳的信号。它们与 Exception 平级,catch(Exception e) 根本捕获不到:
- 绝不写
catch(Error e)并尝试“恢复”——此时logger.error可能因内存满而失败,System.out.println也可能卡住 - 真正做法是在 JVM 启动参数加
-XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_%p.log,配合jstat或 Arthas 定位根源 - 监控侧对
java.lang.Error类型日志自动触发 P0 告警(电话+短信),并强制服务实例下线重启 - 代码里出现
new MyBusinessError extends Error是严重反模式,必须禁止——它混淆了业务逻辑和系统崩溃边界
中间地带:系统异常(Exception)要分层兜底
数据库连不上、RPC 超时、磁盘写满……这些是系统异常,属于 Exception 体系,但既不是业务警告,也不是 Error:
- 对外暴露时统一包装为
SystemUnavailableException(继承 RuntimeException),避免上层堆满 try-catch - 在 DAO 或 Client 层捕获原始 IOException/SQLException,记录 ERROR 日志 + 完整 stackTrace,并附带重试次数、依赖服务名等 MDC 字段
- 配置熔断器(如 Sentinel)对同一异常类型 1 分钟内超 5 次自动降级,降级后返回缓存数据或默认值,同时打 WARN 日志标记“已降级”
- 告警规则设为:ERROR 日志每分钟超 10 条 → 企微机器人通知;连续 3 分钟 ERROR > 0 → 升级电话告警
日志与异常必须协同,不能只看 log level
光靠 log.warn() 打业务警告、log.error() 打系统异常是不够的——日志级别只是输出信号,真正区分靠的是异常对象本身:
- 所有
throw new XxxWarningException(...)必须对应 WARN 日志;所有未捕获的RuntimeException和Exception由全局处理器记为 ERROR;Error则由 JVM 自动记录到 hs_err 文件 - 禁止在业务代码里写
log.error("参数校验失败", e)——这是 INFO 级别事件,e 是 IllegalArgumentException,不该走 ERROR 通道 - Logback 配置中用
<filter class="ch.qos.logback.core.filter.LevelFilter"></filter>严格隔离:ERROR 日志只进 error.log 并对接告警,绝不混入 app.log
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











