顶级开源项目 Sentry 20.x JS-SDK 设计艺术(Unified API篇)

简介: 顶级开源项目 Sentry 20.x JS-SDK 设计艺术(Unified API篇)

统一的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 等)。


简化过的图解


微信图片_20220611153813.png

术语


  • 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

相关文章
|
1月前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
3天前
|
XML API 开发工具
如何接入电竞体育直播API或者SDK
接入电竞体育直播API或SDK的步骤包括:1. 选择合适的API/SDK提供商,如ESL、Riot Games、Twitch等;2. 注册并获取API密钥;3. 阅读官方文档,了解接口调用和数据处理;4. 实现实时更新,确保数据同步;5. 测试与优化,确保功能稳定;6. 遵守使用规范。通过这些步骤,可成功集成电竞直播功能。
|
23天前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
38 12
|
1月前
|
JavaScript NoSQL API
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发如同一座灯塔,指引着数据的海洋。本文将带你航行在Node.js的海域,探索如何从一张白纸到完成一个功能完备的RESTful API。我们将一起学习如何搭建开发环境、设计API结构、处理数据请求与响应,以及实现数据库交互。准备好了吗?启航吧!
|
1月前
|
JavaScript 前端开发 API
Vue.js 3:探索组合式API带来的新变革
Vue.js 3:探索组合式API带来的新变革
|
1月前
|
JavaScript 前端开发 API
Vue.js 3中的Composition API:提升你的组件开发体验
Vue.js 3中的Composition API:提升你的组件开发体验
|
1月前
|
JSON JavaScript API
深入浅出Node.js:从零开始构建RESTful API
【10月更文挑战第39天】 在数字化时代的浪潮中,API(应用程序编程接口)已成为连接不同软件应用的桥梁。本文将带领读者从零基础出发,逐步深入Node.js的世界,最终实现一个功能完备的RESTful API。通过实践,我们将探索如何利用Node.js的异步特性和强大的生态系统来构建高效、可扩展的服务。准备好迎接代码和概念的碰撞,一起解锁后端开发的新篇章。
|
1月前
|
JavaScript 前端开发 API
Vue.js 3:深入探索组合式API的实践与应用
Vue.js 3:深入探索组合式API的实践与应用
|
1月前
|
JSON JavaScript 前端开发
使用JavaScript和Node.js构建简单的RESTful API
使用JavaScript和Node.js构建简单的RESTful API
|
1月前
|
JavaScript 前端开发 NoSQL
深入浅出:使用Node.js构建RESTful API
【10月更文挑战第35天】在数字时代的浪潮中,后端技术如同海洋中稳固的灯塔,为前端应用提供数据和逻辑支撑。本文旨在通过浅显易懂的方式,带领读者了解如何利用Node.js这一强大的后端平台,搭建一个高效、可靠的RESTful API。我们将从基础概念入手,逐步深入到代码实践,最终实现一个简单的API示例。这不仅是对技术的探索,也是对知识传递方式的一次创新尝试。让我们一起启航,探索Node.js的奥秘,解锁后端开发的无限可能。