BLUEPRINT
第 6 章 錯誤與可觀測性
錯誤模型與可觀測性是 Fayger 橫切三層的兩條公共導線。它們讓外部呼叫方能用同一套語意理解失敗、定位失敗、監控執行。
6.1 統一錯誤模型
struct FaygerError {
code: ErrorCode
source_layer: Layer // loader | runtime | adapter | host
message: String
context: Map<String, Value>
cause: Option<Box<FaygerError>>
}
欄位語意:
- code:穩定的、可文件化的識別碼。用於呼叫方分支決策;不在跨版本中變更語意。
- source_layer:標註最初產生錯誤的層。便於路由到對應模組的診斷手冊。
- message:面向人類的描述。短句,不重複 code 資訊。
- context:面向工具的結構化鍵值對。至少包含觸發錯誤的關鍵欄位(offset、missing_fields、current_state、expected_range 等)。
- cause:跨層錯誤的因果鏈。包裝錯誤時把底層錯誤置入 cause,避免吞掉資訊。
6.2 錯誤產生與傳播規則
- 就近建構:每一層在自己的邊界內建構
FaygerError並標註source_layer。 - 逐級包裝:上層若需要補充語意,必須在
cause中保留下層錯誤,不得改寫source_layer。 - 不遺失上下文:
context至少包含觸發錯誤的關鍵欄位。 - 失敗保留:進入
Failed的 BuF_Instance 必須把最末一條 Universal_Instruction 與對應錯誤固化到failure_info中,直到實例被顯式銷毀。
6.3 錯誤鏈範例
一次跨三層的錯誤傳播大致如下:
[top] RT_INSTANCE_FAILED (source_layer = runtime)
└── cause:
[mid] ADP_HOST_CALL_FAILED (source_layer = adapter)
└── cause:
[low] <Host 原生錯誤> (source_layer = host)
呼叫方沿 cause 走訪得到的層序列等於 [runtime, adapter, host],與實際錯誤產生順序的「由內向外」層數對應一致。這條性質在測試中用屬性測試持續驗證。
6.4 關鍵錯誤碼總覽
| 來源層 | 錯誤碼 | 觸發條件 |
|---|---|---|
| Loader | LDR_PARSE_FAIL | BuF 位元組流非法 |
| Loader | LDR_DIGEST_MISMATCH | Section 摘要不匹配(Eager) |
| Loader | LDR_LAZY_DIGEST_MISMATCH | Section 摘要不匹配(Lazy 首次存取) |
| Loader | LDR_SIGNATURE_FAIL | 簽章缺失(強制模式)/ 簽章無效 |
| Loader | LDR_SCHEMA_UNSUPPORTED | schema 版本不在支援範圍 |
| Loader | LDR_RUNTIME_VERSION_TOO_HIGH | Runtime_Interface 最低版本高於當前 |
| Loader | LDR_MISSING_REQUIRED_FIELD | 序列化時 Manifest 缺必填欄位 |
| Loader | LDR_PROFILE_REQUIRED_SECTION_MISSING | 當前 profile 下 Required Section 不可載入 |
| Loader | LDR_LAZY_SOURCE_UNAVAILABLE | Lazy 後續存取 BuF_Source 不可用 |
| Loader | LDR_SOURCE_READ_FAILED | BuF_Source 讀取錯誤(Eager 或 Lazy) |
| Runtime | RT_ILLEGAL_TRANSITION | 非法生命週期遷移 |
| Runtime | RT_IMPL_NOT_FOUND | 路由策略未找到匹配實作 |
| Runtime | RT_IMPL_MISSING_METHODS | 註冊的實作缺必需方法 |
| Runtime | RT_QUOTA_EXCEEDED | 資源使用超限 |
| Runtime | RT_INSTANCE_FAILED | 實例進入 Failed |
| Adapter | ADP_NO_MATCHING_PLATFORM | 沒有適配器匹配當前宿主 |
| Adapter | ADP_UNSUPPORTED_INSTRUCTION | 當前適配器不支援該指令 |
| Adapter | ADP_CAPABILITY_DENIED | 能力被裁剪後不足以執行該指令 |
| Adapter | ADP_HOST_CALL_FAILED | 轉譯為系統呼叫後宿主側失敗 |
載入層錯誤的 context.phase 欄位標註 eager 或 lazy,便於呼叫方按階段路由:啟動期失敗可觸發回退到其他來源、不同 profile 或重試;執行期 Lazy 失敗通常僅影響該 Section 的存取,可由呼叫方決定是否降級。
6.5 可觀測性事件匯流排
interface EventBus {
publish(event: FaygerEvent)
subscribe(filter: EventFilter, handler: EventHandler) -> SubscriptionId
set_debug_enabled(enabled: bool)
}
事件分類:
- Lifecycle Event:BuF_Instance 狀態遷移,攜帶
from、to、timestamp、instance_id。 - Quota Event:資源取樣、超限、暫停。
- Loader Event:載入階段進入 / 完成 / 失敗,攜帶階段名。
- Adapter Event:指令派送、能力裁剪結果、不支援指令記錄。
- Debug Event:僅在
set_debug_enabled(true)時輸出,用於深度排查。
Lifecycle 事件序列一致性
對任意實例的合法遷移序列 [(s₀, s₁), (s₁, s₂), …],事件匯流排發出的事件序列必須滿足:
- 長度相等。
- 每個事件
eᵢ.from == sᵢ₋₁、eᵢ.to == sᵢ。 eᵢ.timestamp單調非遞減。
6.6 與宿主日誌 / 追蹤通道整合
當宿主提供日誌或追蹤通道(systemd-journal、OS Logger、瀏覽器 console、In-App SDK 通道等)時,適配層把 Fayger 內部事件對應到該通道:
- 對應規則由 Platform_Adapter 自描述,執行層無需感知通道差異。
- 事件級別(debug / info / warn / error)對應宿主通道的相應級別。
- Lifecycle Event 與 Loader Event 在 info 級別,Quota / Adapter 錯誤在 warn / error 級別。
如果宿主無日誌通道,事件僅留在 EventBus 內供訂閱者消費。
6.7 除錯事件開關
除錯級事件輸出預設關閉。開啟方式:
event_bus.set_debug_enabled(true)
除錯事件包括:
- 每條 Universal_Instruction 的派送詳情。
- 每次能力裁剪的輸入與輸出。
- 資源監測的每次取樣值。
由於體量大,除錯事件訂閱者應主動篩選、限速或落檔。
6.8 狀態查詢介面
Fayger 必須暴露以下查詢介面,用於維運與診斷:
| 查詢 | 回傳 |
|---|---|
state(instance_id) | 當前 Lifecycle_State |
failure_info(instance_id) | 觸發失敗的最末 Universal_Instruction 與錯誤物件 |
list_implementations() | 已註冊 Runtime_Implementation 描述符列表 |
current_adapter() | 當前選中的 Platform_Adapter 描述符 |
granted_capabilities(instance_id) | 該實例被裁剪後實際擁有的能力集 |
這些介面都是唯讀、冪等、可在任意狀態下呼叫的。
6.9 預設安全姿態
- 強制簽章模式預設 關閉(出於開發便利),但發布版本建議在設定層預設 開啟。
- 除錯事件預設 關閉,需顯式啟用。
- 未宣告的能力對 BuF 預設拒絕。
- 未識別的 Universal_Instruction 類別 預設拒絕,不允許「沉默放行」。
