頂級開源項目 Sentry 20.x JS-SDK 設計藝術(Unified API篇)


SDK 開發

  1. 頂級開源項目 Sentry 20.x JS-SDK 設計藝術(理念與設計原則篇)
  2. 頂級開源項目 Sentry 20.x JS-SDK 設計藝術(開發基礎篇)
  3. 頂級開源項目 Sentry 20.x JS-SDK 設計藝術(概述篇)

系列

  1. Snuba:Sentry 新的搜索基礎設施(基於 ClickHouse 之上)
  2. Sentry 10 K8S 雲原生架構探索,Vue App 1 分鍾快速接入
  3. Sentry(v20.x)玩轉前/后端監控與事件日志大數據分析,使用 Helm 部署到 K8S 集群
  4. Sentry(v20.x) JavaScript SDK 三種安裝加載方式
  5. Sentry(v20.x) JavaScript SDK 配置詳解
  6. Sentry(v20.x) JavaScript SDK 手動捕獲事件基本用法
  7. Sentry(v20.x) JavaScript SDK Source Maps詳解
  8. Sentry(v20.x) JavaScript SDK 故障排除
  9. Sentry(v20.x) JavaScript SDK 1分鍾上手性能監控
  10. Sentry(v20.x) JavaScript SDK 性能監控之管理 Transactions
  11. Sentry(v20.x) JavaScript SDK 性能監控之采樣 Transactions
  12. Sentry(v20.x) JavaScript SDK Enriching Events(豐富事件信息)
  13. Sentry(v20.x) JavaScript SDK Data Management(問題分組篇)

統一的API

新的 Sentry SDK 應遵循 Unified API,使用一致的術語來指代概念。
本文檔說明了 Unified API 是什么以及為什么它存在。

動機

Sentry 有各種各樣的 SDK,這些 SDK 是由不同的開發人員根據不同的想法在過去幾年里開發出來的。這導致了不同 SDK 的特性設置不同,使用不同的概念和術語,這導致了通常不清楚如何在不同的平台上實現相同的東西。

此外,這些 SDK 完全以通過 explicit clients 進行錯誤報告為中心,這意味着通常無法進行某些集成(例如面包屑 breadcrumbs)。

一般准則

  • 我們希望所有 SDK API 的語言/措辭統一,以輔助支持和文檔編制,並使用戶更輕松地在不同環境中使用 Sentry
  • 在設計 SDK 時,我們可以添加一些新的功能,而不是單純的事件報告(transactionsAPM等)。
  • 設計具有相同 client 實例的 SDK,我們既可以通過依賴項注入等在運行時環境中自然工作,也可以使用隱式上下文分派給已經存在的 clientsscopes,以掛接到大多數環境中。 這很重要,因為它允許事件將流程中其他集成的數據包括在內。
  • 常見任務必須簡單明了。
  • 為了幫助第三方庫,“non configured Sentry” 的情況需要快速處理(和延遲執行)。
  • 通用 API 需求在大多數語言中都是有意義的,並且一定不能依賴於超級特殊的構造。 為了使它更自然,我們應該考慮語言細節,並明確地支持它們作為替代方法(disposables, stack guards 等)。

簡化過的圖解

術語

  • minimal:一個單獨的 “facade” 包,它通過接口(interfaces)或代理(proxies)重新導出 SDK 功能的子集。該包不直接依賴於 SDK,相反,如果沒有安裝 SDK,它應該使每個操作都成為 noop。這樣一個包的目的是允許 random 庫記錄面包屑和設置上下文數據,同時不依賴 SDK
  • hub:管理狀態的對象。默認情況下可以使用隱含的 global thread local 或類似的 hubHubs 可以手動創建。
  • scopescope 包含了應該與 Sentry 事件一起隱式發送的數據。它可以保存上下文數據、額外參數、級別覆蓋、指紋等。
  • clientclient 是只配置一次的對象,可以綁定到 hub。然后,用戶可以自動發現 client 並分派對它的調用。用戶通常不需要直接與 client 打交道。它們要么通過 hub 實現,要么通過 static convenience functions 實現。client 主要負責構建 Sentry 事件並將其發送到 transport
  • client options:是特定於語言和運行時的參數,用於配置 client。 這可以是 releaseenvironment,也可以是要配置的 integrationsin-app works 等。
  • contextContextsSentry 提供額外的數據。有特殊的上下文( user 和類似的)和通用的上下文(runtime,os,device),等等。檢查有效鍵的 Contexts。注意:在舊的 SDK 中,您可能會遇到一個與上下文無關的概念,這個概念現在已被作用域棄用。
  • tagsTags 可以是任意 string → 可以搜索事件的 string pairsContexts 被轉換為 tags
  • extraclient users 附加的真正任意數據。這是一個已棄用的特性,但在可預見的未來將繼續得到支持。鼓勵用戶使用上下文代替。
  • transporttransport 是對事件發送進行抽象的客戶端的內部構造。通常,transport 在單獨的線程中運行,並獲取通過隊列發送的事件。transport 負責發送(sending)、重試(retrying)和處理速率限制(handling rate limits)。如果需要,transport 還可能在重啟過程中持久化未發送的事件。
  • integration:向特定框架(frameworks)或環境(environments)提供中間件(middlewares)、綁定(bindings)或鈎子(hooks)的代碼,以及插入這些綁定並激活它們的代碼。集成的使用不遵循公共接口。
  • event processors:針對每個事件運行的回調(Callbacks)。他們可以修改並返回事件,或者可以為 null。返回 null 將丟棄該事件,並且不會進一步處理。有關更多信息,請參見事件管道(Event Pipeline)。
  • disabled SDK:大多數 SDK 功能依賴於已配置的 active client。當有 transport 時,Sentry 認為 clientactive 的。否則,客戶端是 inactive 的,SDK 被認為是 “disabled”。在這種情況下,某些回調函數,例如 configure_scope 或事件處理器(event processors),可能不會被調用。因此,面包屑(breadcrumbs)不會被記錄下來。

"Static(靜態)API"

靜態 API 函數是最常見的面向用戶的 API。用戶只需導入這些功能,即可開始向 Sentry 發出事件或配置作用域。這些快捷方式功能應在包的頂級名稱空間中導出。 他們在后台使用 hubsscopes(有關更多信息,請參見並發性 Concurrency)(如果在該平台上可用)。請注意,下面列出的所有函數大部分都是 Hub::get_current().function 的別名。

  • init(options):這是每個 SDK 的入口點。

通常,這會創建(creates)/重新初始化(reinitializes)傳播到所有新線程(new threads)/執行上下文(execution contexts)的global hub,或者為每個線程(per thread)/執行上下文(execution context)創建一個 hub

接受 optionsdsn 等),配置 client 並將其綁定到當前 hub 或對其進行初始化。應返回一個 stand-in,可用於 drain events(一次性)。

這可能會返回一個 handleguard 來處理。如何實現這一點完全取決於 SDK。這甚至可能是一個 client,如果這對 SDK 有意義的話。在 Rust 中,它是一個 ClientInitGuard,在 JavaScript 中,它可以是一個帶有可等待的 close 方法的 helper 對象。

您應該能夠多次調用此方法,而第二次調用它既可以拆除先前的 client,也可以減少先前 client 的引用計數,等等。

多次調用只能用於測試。如果您在應用程序啟動以外的任何時間調用 init,將會是 undefined

用戶必須調用一次 init,但允許使用禁用的 DSN 進行調用。
例如可能沒有參數傳遞等。

此外,它還設置了所有默認的集成。

  • capture_event(event):接受一個已經組合好的事件,並將其調度到當前活動的中心。 事件對象可以是普通字典或類型化的對象,無論在SDK中更有意義。 它應盡可能遵循本機協議,而忽略平台特定的重命名(案例樣式等)。
  • capture_exception(error):報告 errorexception 對象。根據平台的不同,可能有不同的參數。最明顯的版本只接受一個 error 對象,但在不傳遞 error 且使用當前 exception 的情況下也可能發生變化。
  • capture_message(message, level):報告 message。級別可以是可選的語言默認參數,在這種情況下,它應該默認為 info
  • add_breadcrumb(crumb):向 scope 添加新的面包屑。 如果面包屑的總數超過 max_breadcrumbs 設置,則 SDK 應刪除最舊的面包屑。這與 Hub API 的工作原理類似。如果禁用了 SDK,它應該忽略 breadcrumb
  • configure_scope(callback):可以重新配置 scope 對象調用的回調。這用於為相同范圍內的未來事件附加上下文數據。
  • last_event_id():應該返回當前作用域發出的最后一個事件 ID。例如,這用於實現用戶反饋對話框(feedback)。

並發

所有 SDK 都應具有並發安全上下文存儲(concurrency safe context storage)的概念。 這意味着什么取決於語言。 基本思想是,SDK 的用戶可以調用一種方法來為即將記錄的所有事件安全地提供其他上下文信息。

在大多數語言中,這是作為 thread local stack 實現的,但在某些語言中(比如 JavaScript),它可能是全局的,因為假設這在環境中是有意義的。

以下是一些常見的並發模式:

  • Thread bound hub:在這種模式下,每個 thread 都有自己的 “hub”,該 hub 在內部管理一系列作用域scopes。 如果遵循該模式,則一個 thread(調用 init() 的線程)將成為 “main” hub,該 hub 將用作新生成的線程的基礎,該線程將獲得基於主 hubhub(但又是獨立的)。
  • Internally scoped hub:在一些平台上,如 .NET ambient data 是可用的,在這種情況下 Hub 可以內部管理作用域scopes
  • Dummy hub:在一些平台上,並發性concurrency本身並不存在。在這種情況下,hub 可能完全不存在,或者只是一個沒有並發管理concurrency management的單例。

Hub

在正常情況下,hub 由一堆 clientsscopes 組成。

SDK 維護兩個變量:main hub(一個全局變量)和 current hub(當前線程thead或執行上下文execution context的本地變量,有時也稱為異步本地async local或上下文本地context local變量)

  • Hub::new(client, scope):使用給定的 clientscope 創建一個新的 hubclient 可以在 hubs 之間重用。scope 應歸 hub 所有(如有必要,請進行 clone
  • Hub::new_from_top(hub) / 或者原生構造函數重載native constructor overloads:通過克隆另一個 hub 的頂部堆棧top stack來創建新的 hub
  • get_current_hub() / Hub::current() / Hub::get_current():全局函數或靜態函數以返回當前(線程的)hub
  • get_main_hub() / Hub::main() / Hub::get_main():在主線程main thread是特殊的語言中(“Thread bound hub”模型),這會返回 main thread 的中心而不是當前線程 current thread 的中心。這可能並不存在於所有的語言中。
  • Hub::capture_event / Hub::capture_message / Hub::capture_exception:捕獲 message / exceptioncapture eventcapture_event 將傳遞的 eventscope 數據合並,並分派給 client。作為附加參數,它還需要一個提示。有關 hint 參數,請參見 hints
  • Hub::push_scope():推送一個繼承前一個數據的新作用域層new scope layer。 這應返回有意義的語言的 disposablestack guard。當使用 “內部作用域中心”internally scoped hub 並發模型時,通常需要對此進行調用,否則可能會意外地錯誤共享作用域。
  • Hub::with_scope(callback) (optional):在 Python 中,這可能是上下文管理器;在 Ruby 中,這可能是塊函數。推動並彈出集成工作的 scope
  • Hub::pop_scope(callback) (optional):只存在於沒有更好的資源管理resource management的語言中。最好在 push_scope 的返回值上使用這個函數,或者使用 with_scope。這有時也被稱為 pop_scope_unsafe,以表明不應該直接使用該方法。
  • Hub::configure_scope(callback):使用對修改范圍的可變引用來調用回調。 這也可以是具有它的語言(Python)中的 with 語句。如果沒有 active client 綁定到該 hub,則 SDK 不應調用回調。
  • Hub::add_breadcrumb(crumb, hint):將面包屑添加到當前作用域。
    • 支持的參數應為:
      • 創建面包屑的函數
      • 已經創建的面包屑對象
      • 面包屑列表(可選)
    • 在沒有基本重載形式的語言中,只有原始的面包屑對象raw breadcrumb object應該被接受。
    • 如果沒有 active client 綁定到該 hub,則 SDK 應忽略面包屑。
    • 有關 hint 參數,請參見 hints
  • Hub::client() / Hub::get_client() (optional):返回當前 clientNoneAccessorgetter
  • Hub::bind_client(new_client):將不同的 client 綁定到 hub。如果 hub 也是 init 創建的 client 的所有者,那么如果 hub 是負責處理它的對象,則需要保留對它的引用。
  • Hub::unbind_client() (optional):對於 bind_client 不接受空值的語言,可選的解綁定方法。
  • Hub::last_event_id():應該返回當前 scope 發出的最后一個 event ID。例如,這是用來實現用戶反饋對話框feedback dialogs
  • Hub::run(hub, callback) hub.run(callback), run_in_hub(hub, callback)(optional):運行將 hub 綁定為當前 hub 的回調。

Scope

scope 包含了應該與 Sentry 事件一起隱式發送的數據。它可以保存上下文數據context data、額外參數extra parameters、級別覆蓋level overrides、指紋fingerprints等。

用戶可以通過全局函數 configure_scope 修改當前作用域(設置額外的、標記、當前用戶)。configure_scope 接受一個回調函數,並將當前的作用域傳遞給它。

使用這種基於回調的 API 的原因是效率。如果禁用了 SDK,它就不應該調用回調函數,從而避免不必要的工作。

Sentry.configureScope(scope =>
  scope.setExtra("character_name", "Mighty Fighter"));
  • scope.set_user(user):淺合並用戶 Shallow merges 配置(電子郵件email,用戶名username等)。刪除用戶數據是 SDK 定義的,可以使用 remove_user 函數,也可以不傳遞任何數據。
  • scope.set_extra(key, value):將附加鍵設置為任意值,覆蓋潛在的先前值。 刪除 keySDK 定義的,可以使用 remove_extra 函數或不傳遞任何數據作為數據。這是不推薦使用的功能,應鼓勵用戶改用上下文。
  • scope.set_extras(extras):設置一個具有 key/value 對,便捷功能的對象,而不是多個 set_extra 調用。與 set_extra 一樣,這被視為已棄用的功能。
  • scope.set_tag(key, value):將 tag 設置為字符串值,覆蓋潛在的先前值。 刪除 keySDK 定義的,可以使用 remove_tag 函數或不傳遞任何數據作為數據。
  • scope.set_tags(tags):設置一個具有 key/value 對,便捷功能的對象,而不是多個 set_tag 調用。
  • scope.set_context(key, value):將上下文鍵設置為一個值,覆蓋一個潛在的先前值。刪除 keySDK 定義的,可以使用 remove_context 函數或不傳遞任何數據作為數據。 這些類型是 sdk 指定的。
  • scope.set_level(level):設置在此 scope 內發送的所有事件的級別。
  • scope.set_transaction(transaction_name):設置當前 transaction 的名稱。
  • scope.set_fingerprint(fingerprint[]):將指紋設置為將特定事件分組在一起。
  • scope.add_event_processor(processor):注冊事件處理器函數 event processor。它接受一個事件並返回一個新事件,或者返回 None 來將其刪除。這是許多集成的基礎。
  • scope.add_error_processor(processor)(optional):注冊錯誤處理器函數。 它接受一個事件和異常對象,並返回一個新事件或“None”將其刪除。 這可用於從 SDK 無法提取自身的異常對象中提取其他信息。
  • scope.clear():將 scope 重置為默認值,同時保留所有已注冊的事件處理器event processors。這不會影響子作用域或父作用域。
  • scope.add_breadcrumb(breadcrumb):將面包屑添加到當前 scope
  • scope.clear_breadcrumbs():從 scope 中刪除當前的面包屑 breadcrumbs
  • scope.apply_to_event(event[, max_breadcrumbs]):將 scope 數據應用於給定的事件對象。這也適用於內部存儲在 scope 中的事件處理器 event processors。 一些實現可能想要在此處設置最大面包屑計數。

Client

ClientSDK 中負責事件創建的部分。 例如,Client 應將異常轉換為 Sentry eventClient 應該是無狀態的,它會注入作用域並委托將事件發送到 Transport 的工作。

  • Client::from_config(config):(或者是普通的構造函數)這通常采用帶有 options + dsn 的對象。
  • Client::capture_event(event, scope):通過將事件與其他數據(client 默認設置)合並來捕獲事件。另外,如果將 scope 傳遞到此系統,則來自該范圍的數據會將其傳遞到內部 transport
  • Client::close(timeout):刷新隊列直到超時秒。如果客戶端能夠保證事件的交付僅持續到當前時間點,則首選此方法。這可能會因為超時秒而阻塞。在調用 close 后,客戶端應該被禁用或銷毀。
  • Client::flush(timeout):和 close 的區別一樣,客戶端在調用 flush 后不會被釋放。

Hints

(可選)支持事件捕獲和面包屑添加的附加參數:hint

hint 是特定於 SDK 的,但提供了關於事件起源的高級信息。例如,如果捕獲了一個異常,提示可能攜帶原始異常對象。並不是所有的 SDK 都需要提供這個功能。然而,這個參數是為此目的保留的。

Event Pipeline

capture_event 捕獲的事件將按以下順序處理。

注意:事件可以在任何階段丟棄,此時不會發生進一步的處理。

  1. 如果禁用 SDK, Sentry 會立即丟棄該事件。
  2. 客戶端根據配置的采樣速率對事件進行采樣。事件可以根據抽樣率隨機丟棄。
  3. 使用 apply_to_event 應用該作用域。按順序調用作用域的事件處理器。
  4. Sentry 調用 before-send 鈎子。
  5. Sentry 將事件傳遞到配置的 transport。如果傳輸沒有有效的 DSN,則可以丟棄該事件;它的內部隊列已滿;或由於服務器要求的速率限制。

Options

許多選項都是跨 SDK 標准化的。有關這些選項的列表,請參閱 the main options documentation

我是為少
微信:uuhells123
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM