第 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 錯誤產生與傳播規則

  1. 就近建構:每一層在自己的邊界內建構 FaygerError 並標註 source_layer
  2. 逐級包裝:上層若需要補充語意,必須在 cause 中保留下層錯誤,不得改寫 source_layer
  3. 不遺失上下文context 至少包含觸發錯誤的關鍵欄位。
  4. 失敗保留:進入 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 關鍵錯誤碼總覽

來源層錯誤碼觸發條件
LoaderLDR_PARSE_FAILBuF 位元組流非法
LoaderLDR_DIGEST_MISMATCHSection 摘要不匹配(Eager)
LoaderLDR_LAZY_DIGEST_MISMATCHSection 摘要不匹配(Lazy 首次存取)
LoaderLDR_SIGNATURE_FAIL簽章缺失(強制模式)/ 簽章無效
LoaderLDR_SCHEMA_UNSUPPORTEDschema 版本不在支援範圍
LoaderLDR_RUNTIME_VERSION_TOO_HIGHRuntime_Interface 最低版本高於當前
LoaderLDR_MISSING_REQUIRED_FIELD序列化時 Manifest 缺必填欄位
LoaderLDR_PROFILE_REQUIRED_SECTION_MISSING當前 profile 下 Required Section 不可載入
LoaderLDR_LAZY_SOURCE_UNAVAILABLELazy 後續存取 BuF_Source 不可用
LoaderLDR_SOURCE_READ_FAILEDBuF_Source 讀取錯誤(Eager 或 Lazy)
RuntimeRT_ILLEGAL_TRANSITION非法生命週期遷移
RuntimeRT_IMPL_NOT_FOUND路由策略未找到匹配實作
RuntimeRT_IMPL_MISSING_METHODS註冊的實作缺必需方法
RuntimeRT_QUOTA_EXCEEDED資源使用超限
RuntimeRT_INSTANCE_FAILED實例進入 Failed
AdapterADP_NO_MATCHING_PLATFORM沒有適配器匹配當前宿主
AdapterADP_UNSUPPORTED_INSTRUCTION當前適配器不支援該指令
AdapterADP_CAPABILITY_DENIED能力被裁剪後不足以執行該指令
AdapterADP_HOST_CALL_FAILED轉譯為系統呼叫後宿主側失敗

載入層錯誤的 context.phase 欄位標註 eagerlazy,便於呼叫方按階段路由:啟動期失敗可觸發回退到其他來源、不同 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 狀態遷移,攜帶 fromtotimestampinstance_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 類別 預設拒絕,不允許「沉默放行」。