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