Effect 的错误上报,为什么 Cause 不等于 incident?
把每一个 Effect.fail 都推给告警平台,第一周你会收到几百条「用户不存在」,第二周就没人再看告警了。
Cause 只是「发生过一次失败」的记录,incident 是「值得有人起来处理」的事故;两者之间的过滤要你自己设计,ErrorReporter 提供的是接线点,不是判断。
failure 是预期内的控制流,defect 才是 bug,上报策略要延续这个区分,区别见 Effect 里的 failure 和 defect 怎么区分?。ErrorReporter 本身怎么用,见 Effect 里怎么统一上报错误?,这里只讲它之外、你要自己守的边界。
出错和出事,上报器已经替你分了多少?
在边界处,ErrorReporter 收到 Cause 后会做几件事:对同一个 cause、同一个错误对象去重,自动跳过中断,并读取错误对象上的三个标注,即忽略、严重度、属性。
它没有替你做的是「严重程度」的判断。没有标注严重度的错误,包括 defect,严重度默认是 Info,细节与升级写法见 ErrorReporter 里 defect 为什么也是 Info?。如果你的告警规则按严重度过滤,未标注的 defect 会悄悄滑过去:要么在回调里对 defect 统一升级,要么给领域错误显式标注。
上报器的回调该守哪四条边界?
回调是你和外部系统之间唯一的接口,我对它有四条要求:
- 指纹用稳定的错误
_tag加操作名,不用Cause.pretty或带动态内容的 message,否则同一个问题会裂成无数条。 - 投递与业务结果隔离。上报失败、超时都不能让请求失败,所以回调里只做入队,真正的网络调用放到异步批量里。
- 脱敏发生在 reporter 之前或之内:Authorization、cookie、令牌、请求体不进属性。
- 供应商 SDK 只出现在 reporter 的实现里,领域层只看见
ErrorReporter这个抽象。
第一条是为了让「一个问题一条告警」成立;第二条是为了让可观测性本身不成为新的故障源;第三条与 span 一样,属性必须是显式挑选的,不能顺手把整个请求塞进去。
为什么不把这些写进每个 catch?
因为这四条都是「全站一致」的性质。散落在各个 catch 里,每处各写一遍指纹和脱敏,迟早有一处不一致;放在一个上报器里,换供应商只改这一个实现,业务代码不动。
版本是 effect 4.0.5。去重、跳过中断、读取三个标注、未标注 defect 的严重度默认为 Info,在这个版本上跑过。上面四条边界是我的做法,不是官方规定,其中指纹、入队投递、脱敏没有单独写代码验证。