
SDK 開發
- 頂級開源項目 Sentry 20.x JS-SDK 設計藝術(理念與設計原則篇)
- 頂級開源項目 Sentry 20.x JS-SDK 設計藝術(開發基礎篇)
- 頂級開源項目 Sentry 20.x JS-SDK 設計藝術(概述篇)
系列
- Snuba:Sentry 新的搜索基礎設施(基於 ClickHouse 之上)
- Sentry 10 K8S 雲原生架構探索,Vue App 1 分鍾快速接入
- Sentry(v20.x)玩轉前/后端監控與事件日志大數據分析,使用 Helm 部署到 K8S 集群
- Sentry(v20.x) JavaScript SDK 三種安裝加載方式
- Sentry(v20.x) JavaScript SDK 配置詳解
- Sentry(v20.x) JavaScript SDK 手動捕獲事件基本用法
- Sentry(v20.x) JavaScript SDK Source Maps詳解
- Sentry(v20.x) JavaScript SDK 故障排除
- Sentry(v20.x) JavaScript SDK 1分鍾上手性能監控
- Sentry(v20.x) JavaScript SDK 性能監控之管理 Transactions
- Sentry(v20.x) JavaScript SDK 性能監控之采樣 Transactions
- Sentry(v20.x) JavaScript SDK Enriching Events(豐富事件信息)
- 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時,我們可以添加一些新的功能,而不是單純的事件報告(transactions,APM等)。 - 設計具有相同
client實例的SDK,我們既可以通過依賴項注入等在運行時環境中自然工作,也可以使用隱式上下文分派給已經存在的clients和scopes,以掛接到大多數環境中。 這很重要,因為它允許事件將流程中其他集成的數據包括在內。 - 常見任務必須簡單明了。
- 為了幫助第三方庫,“non configured Sentry” 的情況需要快速處理(和延遲執行)。
- 通用
API需求在大多數語言中都是有意義的,並且一定不能依賴於超級特殊的構造。 為了使它更自然,我們應該考慮語言細節,並明確地支持它們作為替代方法(disposables,stack guards等)。
簡化過的圖解

術語
- minimal:一個單獨的
“facade”包,它通過接口(interfaces)或代理(proxies)重新導出SDK功能的子集。該包不直接依賴於SDK,相反,如果沒有安裝SDK,它應該使每個操作都成為noop。這樣一個包的目的是允許random庫記錄面包屑和設置上下文數據,同時不依賴SDK。 - hub:管理狀態的對象。默認情況下可以使用隱含的
global thread local或類似的hub。Hubs可以手動創建。 - scope:
scope包含了應該與Sentry事件一起隱式發送的數據。它可以保存上下文數據、額外參數、級別覆蓋、指紋等。 - client:
client是只配置一次的對象,可以綁定到hub。然后,用戶可以自動發現client並分派對它的調用。用戶通常不需要直接與client打交道。它們要么通過hub實現,要么通過static convenience functions實現。client主要負責構建Sentry事件並將其發送到transport。 - client options:是特定於語言和運行時的參數,用於配置
client。 這可以是release和environment,也可以是要配置的integrations,in-app works等。 - context:
Contexts為Sentry提供額外的數據。有特殊的上下文(user和類似的)和通用的上下文(runtime,os,device),等等。檢查有效鍵的Contexts。注意:在舊的SDK中,您可能會遇到一個與上下文無關的概念,這個概念現在已被作用域棄用。 - tags:
Tags可以是任意string→ 可以搜索事件的string pairs。Contexts被轉換為tags。 - extra:
client users附加的真正任意數據。這是一個已棄用的特性,但在可預見的未來將繼續得到支持。鼓勵用戶使用上下文代替。 - transport:
transport是對事件發送進行抽象的客戶端的內部構造。通常,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 認為client是active的。否則,客戶端是inactive的,SDK被認為是“disabled”。在這種情況下,某些回調函數,例如configure_scope或事件處理器(event processors),可能不會被調用。因此,面包屑(breadcrumbs)不會被記錄下來。
"Static(靜態)API"
靜態 API 函數是最常見的面向用戶的 API。用戶只需導入這些功能,即可開始向 Sentry 發出事件或配置作用域。這些快捷方式功能應在包的頂級名稱空間中導出。 他們在后台使用 hubs 和 scopes(有關更多信息,請參見並發性 Concurrency)(如果在該平台上可用)。請注意,下面列出的所有函數大部分都是 Hub::get_current().function 的別名。
init(options):這是每個SDK的入口點。
通常,這會創建(creates)/重新初始化(reinitializes)傳播到所有新線程(new threads)/執行上下文(execution contexts)的global hub,或者為每個線程(per thread)/執行上下文(execution context)創建一個 hub。
接受 options(dsn 等),配置 client 並將其綁定到當前 hub 或對其進行初始化。應返回一個 stand-in,可用於 drain events(一次性)。
這可能會返回一個 handle 或 guard 來處理。如何實現這一點完全取決於 SDK。這甚至可能是一個 client,如果這對 SDK 有意義的話。在 Rust 中,它是一個 ClientInitGuard,在 JavaScript 中,它可以是一個帶有可等待的 close 方法的 helper 對象。
您應該能夠多次調用此方法,而第二次調用它既可以拆除先前的 client,也可以減少先前 client 的引用計數,等等。
多次調用只能用於測試。如果您在應用程序啟動以外的任何時間調用 init,將會是 undefined。
用戶必須調用一次 init,但允許使用禁用的 DSN 進行調用。
例如可能沒有參數傳遞等。
此外,它還設置了所有默認的集成。
capture_event(event):接受一個已經組合好的事件,並將其調度到當前活動的中心。 事件對象可以是普通字典或類型化的對象,無論在SDK中更有意義。 它應盡可能遵循本機協議,而忽略平台特定的重命名(案例樣式等)。capture_exception(error):報告error或exception對象。根據平台的不同,可能有不同的參數。最明顯的版本只接受一個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將用作新生成的線程的基礎,該線程將獲得基於主hub的hub(但又是獨立的)。 - Internally scoped hub:在一些平台上,如
.NET ambient data是可用的,在這種情況下Hub可以內部管理作用域scopes。 - Dummy hub:在一些平台上,並發性
concurrency本身並不存在。在這種情況下,hub可能完全不存在,或者只是一個沒有並發管理concurrency management的單例。
Hub
在正常情況下,hub 由一堆 clients 和 scopes 組成。
SDK 維護兩個變量:main hub(一個全局變量)和 current hub(當前線程thead或執行上下文execution context的本地變量,有時也稱為異步本地async local或上下文本地context local變量)
Hub::new(client, scope):使用給定的client和scope創建一個新的hub。client可以在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 / exception到capture event。capture_event將傳遞的event與scope數據合並,並分派給client。作為附加參數,它還需要一個提示。有關hint參數,請參見hints。Hub::push_scope():推送一個繼承前一個數據的新作用域層new scope layer。 這應返回有意義的語言的disposable或stack 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):返回當前client或None的Accessor或getter。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):將附加鍵設置為任意值,覆蓋潛在的先前值。 刪除key是SDK定義的,可以使用remove_extra函數或不傳遞任何數據作為數據。這是不推薦使用的功能,應鼓勵用戶改用上下文。scope.set_extras(extras):設置一個具有key/value對,便捷功能的對象,而不是多個set_extra調用。與set_extra一樣,這被視為已棄用的功能。scope.set_tag(key, value):將tag設置為字符串值,覆蓋潛在的先前值。 刪除key是SDK定義的,可以使用remove_tag函數或不傳遞任何數據作為數據。scope.set_tags(tags):設置一個具有key/value對,便捷功能的對象,而不是多個set_tag調用。scope.set_context(key, value):將上下文鍵設置為一個值,覆蓋一個潛在的先前值。刪除key是SDK定義的,可以使用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
Client 是 SDK 中負責事件創建的部分。 例如,Client 應將異常轉換為 Sentry event。Client 應該是無狀態的,它會注入作用域並委托將事件發送到 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 捕獲的事件將按以下順序處理。
注意:事件可以在任何階段丟棄,此時不會發生進一步的處理。
- 如果禁用
SDK,Sentry會立即丟棄該事件。 - 客戶端根據配置的采樣速率對事件進行采樣。事件可以根據抽樣率隨機丟棄。
- 使用
apply_to_event應用該作用域。按順序調用作用域的事件處理器。 Sentry調用before-send鈎子。Sentry將事件傳遞到配置的transport。如果傳輸沒有有效的DSN,則可以丟棄該事件;它的內部隊列已滿;或由於服務器要求的速率限制。
Options
許多選項都是跨 SDK 標准化的。有關這些選項的列表,請參閱 the main options documentation。
我是為少
微信:uuhells123
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)
