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
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)