可观测性与错误链
领域事实、Observation、Telemetry、Error Occurrence、Alert、Incident 与对账的边界
OceanWay 的诊断能力来自多种证据协作,而不是把所有数据塞进一张日志表。领域事实回答“业务已经提交了什么”,Observation 回答“某个边界看到了什么”,Telemetry 回答“调用怎样发生”,Audit 回答“谁以何种授权做了什么”。
证据分层
evidenceKind | 权威范围 | 允许丢失/采样 | 普通保留内容 |
|---|---|---|---|
domain_fact | 对应领域状态变化 | 不允许 | 稳定 ID、状态、金额/单位、版本与低敏关联 |
operational_observation | 来源曾看到的运行现象 | 首期允许 best-effort | 来源、阶段、结果、确定性、规范化错误 |
operations_runtime | 运维管道自身状态 | 不允许静默丢失 | Delivery、Attempt、Receipt、Build、Gap、Quarantine |
telemetry_reference | 受控引用指向 Logs/Trace | 允许按策略丢失或采样 | 引用、时间与低敏关联,不复制原始内容 |
derived_summary | 从已标注证据计算的摘要 | 可重建 | 最可信边界、完整性与影响摘要 |
Metrics 与 Audit 是旁路数据面,不是第六、第七种 evidenceKind:
| 旁路数据面 | 作用 | 可靠性 | 普通保留内容 |
|---|---|---|---|
| Metrics | 趋势、SLO、容量和告警 | 聚合 | 低基数维度 |
| Audit | 特权主体、授权和命令证据 | 不允许 | Workforce、Grant、资源、动作、结果 |
Trace 被采样、日志过期或 Observation 丢失不能导致 Reservation、Usage、Ledger、Run 或 Asset 事实消失。反过来,存在一个 Log 行也不能证明领域事务已提交。
标识符职责
requestId
单次入口请求;Replay 每次都不同
traceId / spanId
一次可采样处理链;异步恢复通常创建新 Trace 并使用 Span Link
correlationId / operationId
跨重试、轮询、恢复和补偿的稳定业务关联
runId / reservationId / eventId
executionAttemptId / deliveryAttemptId
gatewayInvocationId / gatewayTaskId / providerAttemptId
各领域、投递和私有网关不可猜测的独立稳定身份;禁止使用裸 attemptId 混合命名空间不能为了查询方便把所有含义压进 traceId。Provider 不支持 Trace 时,Gateway 只保存本地映射并报告“未透传”,不能伪造端到端 Trace。
Error Occurrence
Operations 投影中的 ErrorOccurrence 是规范化错误事实或观测摘要。它先包含下列公共字段,再按 evidenceKind 使用严格判别联合:
errorId
occurredAt? / sourceClaimedObservedAt? / acceptedAt? / derivedAt?
source / evidenceKind
layer / phase
normalizedCode / severity
certainty / retryDisposition / compensationDisposition
messageTemplateKey / sanitizedParameters
causeErrorId?
requestId? / traceId? / correlationId? / operationId?
runId? / runStepId? / executionAttemptId?
gatewayPool? / gatewayDeploymentId?
gatewayInvocationId? / gatewayTaskId?
attemptRouteBindingRef? / attemptRouteBindingSchemaVersion?
attemptRouteBindingDigestAlgorithmVersion? / attemptRouteBindingDigest?
gatewayRouteSnapshotRef? / gatewayRouteSnapshotSchemaVersion?
gatewayRouteSnapshotDigestAlgorithmVersion? / gatewayRouteSnapshotDigest?
reservationId? / eventId? / observationId?
technicalDetailRef?evidenceKind 必须严格取 domain_fact | operational_observation | operations_runtime | telemetry_reference | derived_summary。Dispatcher/Projector 自身状态由 Core Query 直接组合,不递归产生新的 Domain Event。首期只有 Admission 与 Reservation 基线,没有 Gateway 执行、Output、Asset、Metering 或 Settlement,因此不能产生这些阶段的成功或失败事实;对应阶段显示 not_yet_supported。
对于 operational_observation,Gateway执行字段只能来自同一条 Accepted ErrorOccurrence@1.gatewayExecution。Binding创建或 Provider Side Effect可能发生后,Run/Step/Attempt、Pool/Deployment、Binding/Route完整四元组与严格互斥的 Invocation/Task Anchor必须全部存在并经 Core交叉验证;四元组半联合、未知算法、摘要错配、缺项或多 Attempt错绑都进入 Quarantine,Projector不从相邻记录补齐。其他 evidenceKind只能展示其事实 Owner明确提供的关联。
Fingerprint 字段只属于 Accepted Error Observation 投影:
operational_observation {
observationId / acceptedAt
source.service / surface
errorNormalizationPolicyVersion
errorFingerprintVersion
errorFingerprint
}
domain_fact | operations_runtime | telemetry_reference | derived_summary {
errorNormalizationPolicyVersion = forbidden
errorFingerprintVersion = forbidden
errorFingerprint = forbidden
}source.service 是 Observation 分支唯一规范服务字段;公共 DTO 不再重复提供顶层 service。后四类从各自判别 Source 对象展示 Owner/Service Lineage,但不得进入 Error Fingerprint v1 Group,也不得从相邻 Observation、当前 Deployment 或日志补齐指纹字段。未来如需跨 Evidence Kind 分组,必须发布新的分组契约与算法版本,不能放宽 v1 联合。
四个时间字段互不回填,权威范围由 evidenceKind 固定:
evidenceKind | 时间字段约束 | 可以证明什么 |
|---|---|---|
domain_fact | occurredAt 必填;其他三项缺失 | Domain Owner 在事务中记录的领域发生时间;跨 Aggregate 因果仍以事件因果边和 Revision 为准 |
operational_observation | Core acceptedAt 必填;sourceClaimedObservedAt 可选;occurredAt/derivedAt 缺失 | Core 已在何时接受该 Observation;来源自报时间只是一项可见 Claim |
operations_runtime | Core 生成的 occurredAt 必填;其他三项缺失 | Delivery、Receipt、Gap、Quarantine 等运维对象在 Core 内的发生时间 |
telemetry_reference | derivedAt 必填;sourceClaimedObservedAt 可选;其他两项缺失 | 引用何时生成以及遥测来源声明的时间;两者都不证明领域状态发生时间 |
derived_summary | derivedAt 必填;其他三项缺失;输入时间通过 Field Evidence 保留 | 摘要在何时基于哪组证据计算,不能生成新的 occurredAt 或 acceptedAt |
Projector 必须拒绝或隔离不符合该矩阵的组合,不能用数据库默认值把缺失的来源时间补成 occurredAt,也不能用 derivedAt 代替 Observation 的 acceptedAt。
Observation 时间信任
sourceClaimedObservedAt 来自 Producer,可因时钟漂移、离线缓冲或错误配置早于或晚于真实处理时间。它只在 Timeline 中与 Source 标识并列展示,不进入 Operation Summary、Error Group 的首次/最近时间、查询游标或任何自动决策。
所有 Accepted Observation 的稳定排序使用 (acceptedAt, observationId);accepted health window 的起止和最后活动时间、Observation Payload 的 Retention/Archive 资格、Observation Freshness 以及 Operation Summary 的 first_accepted_observation_at/latest_accepted_observation_at 都只能从 Core 注入的 acceptedAt 计算。相同 acceptedAt 用 observationId 打破并列。来源声明时间即使位于未来、远早于保留边界或与接收顺序相反,也不得改变上述结果。
messageTemplateKey 只能引用 Contracts 注册的稳定模板;sanitizedParameters 使用逐字段白名单、类型和长度约束,并在输出时按目标上下文编码。它们不能退化为自由文本错误袋。原始 Provider/堆栈信息只允许通过 Case-scoped technicalDetailRef 访问。
提交确定性
错误必须保留提交确定性:
rejected_before_dispatch:在尝试外部提交前明确拒绝。not_submitted:可信地确认没有提交目标系统。submitted:已提交但尚无终态。provider_accepted:目标明确接受。unknown:无法确认是否提交。
当前 API Edge 到 Core 的超时可以形成 admission_outcome_unknown Observation,但最终是否准入只能由 Core Domain Fact 或携带同一 Idempotency-Key 的受控重试确认。unknown 不能被页面自动映射为 failed,也不能触发换 Key 再提交。
Error Fingerprint
Fingerprint 由 Core Operations Projector 按 oceanway-contracts 的唯一版本化算法计算;Producer、Gateway 和日志提交的现成 Fingerprint 不可信,也不能进入规范分组。每条结果同时保存 errorNormalizationPolicyVersion + errorFingerprintVersion + errorFingerprint,旧版本分组不能与新版本静默合并。
版本 1 使用稳定、低敏、低基数字段:
service + layer + phase + normalizedCode
+ surface完整输入键、UTF-8 NFC 规范化、RFC 8785 Canonical JSON、SHA-256 与 efp1_ 编码以及固定测试向量以事件与 Observation 契约为唯一准绳。v1 不通过相邻记录补入 Capability、Deployment、Config 或位置。不得加入 Request ID、User/Tenant ID、Prompt、完整错误正文、动态 URL、签名参数、Provider Task ID 或时间戳。Fingerprint Group 只是相关错误聚合,不自动证明根因;发布与错误时间相关也只能标为“相关候选”。
安全内容分层
| 字段 | 面向对象 | 内容规则 |
|---|---|---|
customerMessage | 用户/开发者 | 安全、稳定、可执行,不暴露内部路由与供应商细节 |
operatorSummary | 有职责授权的 Workforce | 脱敏阶段、确定性、影响和建议检查项 |
technicalDetailRef | 获得 Case/JIT 调试权限的人员 | 指向加密、限时、受审计证据,不复制到普通页面 |
普通 Event、Observation、Log、Trace、Metric 和 Error 都禁止包含:
- Prompt、System Prompt、输入/输出正文、媒体与 Base64;
- Authorization、DeveloperCredential Secret/Digest、Cookie、Session 和 Provider Credential;
- 签名 URL、支付凭据、Callback Secret 和 MCP Secret;
- 未脱敏 Provider 错误页、完整请求/响应 Body 与堆栈;
- 不必要的邮箱、手机号、完整 IP、User-Agent 或支付信息。
如果 Incident 确实需要原始证据,只能保存到加密、Case-scoped、限时访问的受限存储,并产生访问 Audit。保留期和审批条件来自数据治理策略。
Metrics 与告警
本阶段的低基数 Metrics 包括:
- Outbox Backlog、Oldest Age、Claim、Retry、Lease Expiry、Quarantine;
- Consumer Ack、Duplicate Receipt、Schema Reject、Revision Gap;
- Projection Lag、Freshness、Shadow Rebuild 与 Query 可用性;
- Observation Intake 接受、拒绝和来源可用性;
- 按 Service、Environment、Region、Event Type、Schema Version、Status 和 Normalized Error Code 聚合的结果。
禁止把 userId、tenantId、requestId、traceId、operationId、runId、eventId、reservationId 或 observationId 放进 Metric Label。需要定位单项时通过授权的 Exact Identifier Index,而不是高基数指标。
告警阈值、SLO Burn、积压窗口和容量上限来自客户承诺、容量测试与管理员配置。本架构不规定无证据的固定阈值。
Alert、Incident 与 Reconciliation
Error Occurrence
→ Fingerprint Group
→ SLO / Rule Evaluation
→ Alert Instance
→ Incident(存在实际或高风险客户影响时)
→ Recovery / Reconciliation Evidence- 单次错误不必产生 Alert,Alert 也不自动等于 Incident。
- Outbox Quarantine、Projection Gap 或 Observation Intake 健康下降首先是运维数据链问题;页面必须避免据此断言客户 Run 已失败。Edge 没有 Durable Spool 或连续 Sequence,不能从已接受记录推算请求 Coverage 或丢失数量。
- Incident 绑定 Owner、影响范围、时间线、变更关联、缓解、验证和后续事项。
- 人工恢复、未来事件重投和任何领域补偿必须走 Admin Command、JIT/Approval、幂等与 Audit;首期 Explorer 只读。
- Incident 关闭前检查受影响 Run、Reservation、Projection Gap 和 Quarantine;不能只看到 Metric 恢复就结束。
Gateway 执行、Output/Asset、Metering 与 Settlement 当前均未实施。未来各阶段接入后会沿用同一错误与证据模型扩展新的稳定领域 ID;在此之前不会通过读取私有 Gateway 表或抓取日志来补成权威状态。