让建站和SEO变得简单

让不懂建站的用户快速建站,让会建站的提高建站效率!

你的位置:福彩8 > 首页 >

Sentry 斥地者孝顺指南

发布日期:2022-03-13 19:45    点击次数:200

本文档的方向是将 Sentry SDK 中性能监控功能的演变置于高下文中。咱们动身点归来了如何将性能监控添加到 Sentry 和 SDK, 然后咱们商榷 identified issues(已笃定的问题) 吸取的阅历阅历以及科罚这些问题的举措。

先容

早在 2019 岁首,Sentry 就运行尝试向 SDK 添加追踪功能。 Python 和 JavaScript SDK 是想象和斥地第一个成见的测试平台。成见考证于 2019 年 4 月 29 日 发布, 并于 2019 年 5 月 7 日寄托给 Sentry。 Python 和 JavaScript 是不言而喻的选拔,因为它们允许咱们检修检测 Sentry 我方的后端和前端。

https://github.com/getsentry/sentry-python/pull/342 https://github.com/getsentry/sentry-javascript/pull/1918 https://github.com/getsentry/sentry-python/releases/tag/0.7.13 https://github.com/getsentry/sentry/pull/12952

请重视,上述服务与 OpenCensus 和 OpenTracing 合并酿成 OpenTelemetry 是同期代的。 Sentry 的 API 和 SDK 杀青鉴戒了 OpenTelemetry 1.0 之前版块的灵感,并迎合了咱们我方的主张。举例,咱们的 Span 情状列表与 2019 年底足下在 OpenTelemetry 范例中不错找到的匹配。

https://medium.com/opentracing/a-roadmap-to-convergence-b074e5815289 https://github.com/getsentry/relay/blob/55127c75d4eeebf787848a05a12150ee5c59acd9/relay-common/src/constants.rs#L179-L181

使用 API 后,性能监控支援随后推广到其他 SDK。Sentry 的性能监控 科罚决策于 2020 年 7 月广泛可用。 OpenTelemetry 的追踪范例 1.0 版于 2021 年 2 月发布。

https://blog.sentry.io/2020/07/14/see-slow-faster-with-performance-monitoring https://medium.com/opentelemetry/opentelemetry-specification-v1-0-0-tracing-edition-72dd08936978

咱们领先的杀青重用了咱们现存的弱点讲明机制:

Event type 推广了新字段。这意味着咱们不错勤俭时刻并快速运行向 Sentry 发送事件,而不是想象和杀青全新的接管管道,这一次,不是 error,而是一种新的 transaction 事件类型。 https://develop.sentry.dev/sdk/event-payloads/ 由于咱们仅仅发送一种新式事件,因此也重用了 SDK 传输层。 由于咱们分享接管管道(ingestion pipeline),这意味着咱们分享存储以及发生在通盘事件上的处理的许多部分。

咱们的杀青演变成明确强调 Transaction 和 Span 之间的区别。部分原因是重用 Event 接口的反作用。

Transaction 与客户产生了考究的共识。他们允许杰出高傲代码中的蹙迫服务块,举例浏览器页面加载或 http 服务器肯求。客户不错检察和浏览 transaction 列表,而在 transaction 中,span 为更细粒度的服务单元提供考究无比的时刻安排。

鄙人一节中,咱们将商榷面前模子的一些污点。

已笃定的问题

天然长入 SDK 架构(hub、client、scope) 和 transaction ingestion 模子的重用有其优点,但阅历揭示了一些咱们将其分为两类的问题。

https://develop.sentry.dev/sdk/unified-api/

第一组与 scope 传播关联,本质上是笃定 面前 scope 是什么的才能。用户代码中的手动检测以及 SDK 集成中的自动检测都需要此操作。

第二组是与用于将 transaction 数据从 SDK 发送到 Sentry 的 wire 方式关系的问题。

Scope 传播

该问题由 getsentry/sentry-javascript#3751 追踪。

https://github.com/getsentry/sentry-javascript/issues/3751

Unified SDK 架构 基本上是基于每个并发单元存在一个 hub,每个 hub 有一堆 client 和 scope 对。

https://develop.sentry.dev/sdk/unified-api/

Client 保存建树并厚爱通过 transport 向 Sentry 发送数据,而 scope 保存附加到传出事件(举例 tag 和 breadcrumb)的高下文数据。

每个 hub 都澄莹面前的 scope 是什么。它长久是堆栈顶部的 scope。珍摄的部分是 “per unit of concurrency(每单元并发)” 有一个 hub。

举例,JavaScript 是具有事件轮回和异步代码践诺的单线程。莫得圭表的方法来承载跨异门径用服务的高下文数据。因此,关于 JavaScript 浏览器欺诈设施,只须一个全局 hub 分享用于同步和异步代码。

雷同的情况出当今 Mobile SDK 上。用户盼愿高下文数据(举例 tags、current user 是什么、 breadcrumbs 以及存储在 scope 上的其他信息)不错从任何线程取得和建立。因此,在这些 SDK 中,只须一个全局 hub。

在这两种情况下,当 SDK 必须处理 reporting errors 时,一切都相对较好。跟着追踪 transaction 和 span 的额外服务,scope 变得不稳健存储面前的 span,因为它驱逐了并发 span 的存在。

关于浏览器 JavaScript,一个可能的科罚决策是使用 Zone.js,Angular 框架的一部分。主要挑战是它加多了包的大小,况兼可能会或然中影响最终用户欺诈设施,因为它对 JavaScript 运行时引擎的环节部分进行了山公修补(monkey-patches)。

https://github.com/angular/angular/blob/master/packages/zone.js/README.md

当咱们尝试为手动检测创建更浅薄的 API 时,scope 传播问题变得尤为彰着。这个主张是公开一个 Sentry.trace 函数,该函数将隐式传播 tracing 和 scope 数据, 并支援同步和异步代码的深度嵌套。

举个例子,假定有人想测量搜索 DOM 树需要多万古刻。Tracing(追踪) 此操作将如下所示:

await Sentry.trace(   {     op: 'dom',     description: 'Walk DOM Tree',   },   async () => await walkDomTree() ); 

使用 Sentry.trace 功能,用户在添加计时数据时不消顾忌保留对正确 transaction 或 span 的援用。用户不错在 walkDomTree 函数中解放创建子 Span,Span 将在正确的档次结构中排序。

本质 trace 函数的杀青相对浅薄 (参见具有示例杀青的 PR)。然则,了解异步代码和全局集成中确面前 span 是一个尚未克服的挑战。

https://github.com/getsentry/sentry-javascript/pull/3697/files#diff-f5bf6e0cdf7709e5675fcdc3b4ff254dd68f3c9d1a399c8751e0fa1846fa85dbR158

以下两个示例轮廓了 scope 传播问题。

无法笃定面前 Span

洽商一些需要获取对面前 span 的援用的自动检测代码,在这种情况下,手动 scope 传播不可用。

// SDK code function fetchWrapper(/* ... */) {   /*     ... some code omitted for simplicity ...   */   const parent = getCurrentHub().getScope().getSpan(); // <1>   const span = parent.startChild({     data: { type: 'fetch' },     description: `${method} ${url}`,     op: 'http.client',   });   try {     // ...     // return fetch(...);   } finally {     span.finish();   } } window.fetch = fetchWrapper;  // User code async function f1() {   const hub = getCurrentHub();   let t = hub.startTransaction({ name: 't1' });   hub.getScope().setSpan(t);   try {     await fetch('https://example.com/f1');   } finally {     t.finish();   } } async function f2() {   const hub = getCurrentHub();   let t = hub.startTransaction({ name: 't2' });   hub.getScope().setSpan(t);   try {     await fetch('https://example.com/f2');   } finally {     t.finish();   } } Promise.all([f1(), f2()]); // run f1 and f2 concurrently 

在上头的例子中,几个并发的 fetch 肯求触发了 fetchWrapper helper 的践诺。行 <1> 必须或者凭证面前的践诺过程洞悉到不同的 span,导致如下两个 span 树:

t1 \   |- http.client GET https://example.com/f1 t2 \   |- http.client GET https://example.com/f2 

这意味着,当 f1 运行时,parent 必须援用 t1,而当 f2 运行时,parent 必须是 t2。厄运的是,上头的通盘代码都在不甘人后地更新和读取单个 hub 实例,因此洞悉到的 span 树不是笃定性的。举例,驱逐可能弱点地为:

t1 t2 \   |- http.client GET https://example.com/f1   |- http.client GET https://example.com/f2 

当作无法正确笃定面前 span 的反作用, fetch 集成的高傲杀青(和其他)在JavaScript 浏览器 SDK 中选拔创建 flat transactions, 其中通盘子 span 都是 transaction 的平直子代(而不是具有相宜的多级树结构)。

https://github.com/getsentry/sentry-javascript/blob/61eda62ed5df5654f93e34a4848fc9ae3fcac0f7/packages/tracing/src/browser/request.ts#L169-L178

请重视,其他追踪库也濒临相通的挑战。在 OpenTelemetry for JavaScript 中有几个(在洞开时)问题与笃定父跨度和正确的高下文传播(包括异步代码)关系:

若是使用多个 TracerProvider 实例,则高下文露馅 #1932

https://github.com/open-telemetry/opentelemetry-js/issues/1932

如安在不传递 parent 的情况下创建嵌套 span #1963

https://github.com/open-telemetry/opentelemetry-js/issues/1963

嵌套的子 span 莫得得到正确的父级 #1940

https://github.com/open-telemetry/opentelemetry-js/issues/1940

OpenTracing shim 不会更正高下文 #2016

https://github.com/open-telemetry/opentelemetry-js/issues/2016

Http Span 未迎合/未建立父 Span #2333

https://github.com/open-telemetry/opentelemetry-js/issues/2333

互相冲破的数据传播预期

每当咱们添加前边商榷过的 trace 函数,或者仅仅尝试使用 Zones 科罚 scope 传播时,就会出现预期冲破。

面前的 span 与 tags、breadcrumbs 等沿途存储在 scope 中的事实使数据传播变得繁杂, 因为 scope 的某些部分旨在仅传播到里面函数调用中(举例,tags), 而其别人预测会传播回调用者(举例,breadcrumbs),尤其是在出现 error 时。

这是一个例子:

function a() {   trace((span, scope) => {     scope.setTag('func', 'a');     scope.setTag('id', '123');     scope.addBreadcrumb('was in a');     try {       b();     } catch(e) {       // How to report the SpanID from the span in b?     } finally {       captureMessage('hello from a');       // tags: {func: 'a', id: '123'}       // breadcrumbs: ['was in a', 'was in b']     }   }) }  function b() {   trace((span, scope) => {     const fail = Math.random() > 0.5;     scope.setTag('func', 'b');     scope.setTag('fail', fail.toString());     scope.addBreadcrumb('was in b');     captureMessage('hello from b');     // tags: {func: 'b', id: '123', fail: ?}     // breadcrumbs: ['was in a', 'was in b']     if (fail) {       throw Error('b failed');     }   }); } 

在上头的示例中,若是 error 在调用堆栈中冒泡,咱们但愿或者讲明 error 发生在哪个 span(通过援用 SpanID)。咱们但愿有面包屑来描画发生的一切,不管哪个 Zones 正在践诺, 咱们但愿在里面 Zone 中建立一个 tag 来遮盖来自父 Zone 的同名 tag, 同期领受来自父 Zone 的通盘其他 tag。每个 Zone 都有我方的 "current span"。

通盘这些不同的盼愿使得很难以一种不错贯通的神态重用面前的 scope 成见、面包屑的记载神态以及这些不同的成见如何互相作用。

临了,值得重视的是,在不禁闭现存 SDK API 的情况下,重组 scope 料理的革新很可能无法完成。现存的 SDK 成见 — 如 hubs、scopes、breadcrumbs、user、tags 和 contexts — 都必须从头建模。

Span 接管模子

洽商由以下 span 树描画的追踪:

F* ├─ B* │  ├─ B │  ├─ B │  ├─ B │  │  ├─ S* │  │  ├─ S* │  ├─ B │  ├─ B │  │  ├─ S* │  ├─ B │  ├─ B │  ├─ B │  │  ├─ S*  where F: span created on frontend service B: span created on backend service S: span created on storage service 

此追踪阐述了 3 个被检测的服务,当用户单击网页上的按钮 (F) 时,后端 (B) 践诺一些服务,然后需要对存储服务 (S) 进行屡次查询。位于给定服务进口点的 Span 标有 * 以示意它们是 transaction。

咱们不错通过这个例子来比较和贯通 Sentry 的 span 接管模子与 OpenTelemetry 和其他雷同追踪系统使用的模子之间的区别。

在 Sentry 的 span 接管模子中,属于 transaction 的通盘 span 必须在单个肯求中沿途发送。这意味着在通盘 B* transaction 时刻,通盘 B span 都必须保存在内存中,包括鄙人游服务(示例中的存储服务)上消费的时刻。

在 OpenTelemetry 的模子中,span 在完成时被沿途批处理,况兼一朝 a) 批次中有一定数目的 span 或 b) 过了一定的时刻就会发送批次。在咱们的示例中,这可能意味着前 3 个 B 跨度将沿途批处理并发送, 而第一个 S* 事务仍在存储服务中进行。随后,其他 B span 将沿途批处理并在完成时发送,直到最终 B* transaction span 也被发送。

天然 transaction 当作将 span 组合在沿途并探索 Sentry 中感敬爱的操作的一种神态突出灵验, 但它们目下存在的体式会带来额外的融会背负。 SDK 诊治人员和最终用户在编写检测代码时都必须了解并在 transaction 或 span 之间进行选拔。

在面前的接管模子中照旧笃定了接下来几节中的问题,况兼都与这种二分法关联。

事务的复杂 JSON 序列化

在 OpenTelemetry 的模子中, 通盘跨度都撤职调换的逻辑方式。用户和检测库不错通过将 key-value 属性附加到任何 span 来为其提供更多含义。 wire 契约使用 span 列表将数据从一个系统发送到另一个系统。

https://github.com/open-telemetry/opentelemetry-proto/blob/ebef7c999f4dea62b5b033e92a221411c49c0966/opentelemetry/proto/trace/v1/trace.proto#L56-L235

与 OpenTelemetry 不同,Sentry 的模子对两种类型的 span 进行了严格区别:transaction span(频繁称为 transactions)和 regular span。

在内存中,transaction span 和 regular span 有一个区别:transaction span 有一个额外的属性,即 transaction name。

但是,当序列化为 JSON 时,各异更大。 Sentry SDK 以平直雷同于内存中的 span 的方式将成例 span 序列化为 JSON。比拟之下,transaction span 的序列化需要将其 span 属性映射到 Sentry Event (领先用于 report errors,推广为特意用于 transactions 的新字段),并将通盘子 span 当作列表镶嵌 Event 中。

Transaction Span 获取 Event 属性

当 transaction 从其内存示意调遣为 Event 时, 它会取得更多无法分派给 regular span 的属性, 举例 breadcrumbs, extra, contexts, event_id, fingerprint, release, environment, user 等。

生命周期钩子

Sentry SDK 为 error 事件公开了一个 BeforeSend hook,允许用户在将事件发送到 Sentry 之前修改和/或丢弃事件。

当引入新的 transaction 类型事件时,很快就决定此类事件不融会过 BeforeSend hook,主要有两个原因:

防护用户代码依赖 transaction 的双重体式(有时看起来像一个 span,有时像一个 event,如前几节所述); 为了防护现存的 BeforeSend 函数在编写时只洽商到 error 而干预 transaction,不管是或然地更正它们、充足丢弃它们,照旧导致一些其他出人想到的反作用。

然则,也很彰着需要某种体式的 lifecycle hook,以允许用户践诺诸如更新 transaction 称呼之类的操作。

咱们最终达成了中间态度,即通过使用 EventProcessor(一种更通用的 BeforeSend 体式)来允许革新/丢弃 transaction 事件。这通过在数据离开 SDK 之前让用户立即看望他们的数据来科罚问题,但它也有污点,它比 BeforeSend 使用起来更复杂,况兼还浮现了从未运筹帷幄露馅的 transaction 二元性。

比拟之下,在 OpenTelemetry 中,span 通过 span processor,这是两个生命周期钩子:一个是在 span 运行时,一个是在它收尾时。

嵌套事务

Sentry 的接管模子不是为服务中的嵌套 transaction 而想象的。Transaction 旨在标志服务调遣。

在实践中,SDK 无法防护 transaction 嵌套。最终驱逐可能会让用户感到惊诧,因为每笔 transaction 都会运行一棵新树。关联这些树的唯独方法是通过 trace_id。

Sentry 的计费模子是针对每个事件的,不管是 error 事件照旧 transaction 事件。这意味着 transaction 中的 transaction 会生成两个可计郁闷件。

在 SDK 中,在 transaction 中进行 transaction 将导致里面 span 被围绕它们的最内层 transaction “统一”。在这些情况下,创建 span 的代码只会将它们添加到两个 transaction 之一,从而导致另一个 transaction 中的检测裂缝。

Sentry 的 UI 并非旨在以灵验的神态处理嵌套 transaction。当检察任何一个 transaction 时,就好像 transaction 中的通盘其他 transaction 都不存在(树视图上莫得平直示意其他 transaction)。有一个 trace view 功能来可视化分享一个 trace_id 的通盘 transaction, 但 trace view 仅通过高傲 transaction 而不是子 span 来提供追踪的概述。若是不先看望某个 transaction,就无法导航到 trace view。

关于这种情况(伪代码),用户对 UI 中的盼愿也存在污染:

# if do_a_database_query returns 10 results, is the user #   - seeing 11 transactions in the UI? #   - billed for 11 transactions? #   - see spans within create_thumbnail in the innermost transaction only? with transaction("index-page"):     results = do_a_database_query()     for result in results:         if result["needs_thumbnail"]:             with transaction("create-thumbnail", {"resource": result["id"]}):                 create_thumbnail(result) 
跨度不成存在于事务以外

Sentry 的追踪体验充足围绕着存在于 transaction 中的 trace 部分。这意味着数据不成存在于 transaction 以外,即使它存在于 trace 中。

若是 SDK 莫得进行 transaction,则由 instrumentation 创建的 regular span 将充足丢失。也即是说,这对 Web server 来说不是什么问题,因为自动检测的 transaction 跟着每个传入肯求运行和收尾。

Transaction 的要求在前端(浏览器、转移和桌面欺诈设施)上尤其具有挑战性, 因为在这些情况下,自动检测的 transaction 不太可靠地拿获通盘 span,因为它们在自动完成之前只接续有限的时刻。

在 trace 以仅当作 span 而不是 transaction 进行检测的操作运行的情况下,会出现另一个问题。在咱们的 示例追踪中,产生 trace 的第一个 span 是由于单击按钮。若是按钮点击 F* 被检测为成例的 span 而不是 transaction,则很可能不会拿获来自前端的数据。然则,仍会拿获 B 和 S span,导致不齐备的思路。

在 Sentry 的模子中,若是一个 span 不是一个 transaction 况兼莫得当作 transaction 的先人 span,那么该 span 将不会被接管。反过来,这意味着在很厚情况下,追踪丢失了有助于调试问题的环节信息,突出是在前端,transaction 需要在某个时刻收尾但践诺可能会连接。

自动和手动检测濒临着决定是运行 span 照旧 transaction 的挑战,洽商到以下身分,决定尤其珍摄:

若是莫得 transaction,则 span 丢失。

若是照旧存在 transaction,则存在嵌套事务问题。

穷乏 Web Vitals 测量

Sentry 的浏览器器具汇集 Web Vitals 测量值。但是,因为这些测量值是使用自动检测的 transaction 当作载体发送到 Sentry 的,是以在自动 transaction 完成后由浏览器提供的测量值将丢失。

这会导致 transaction 丢失一些 Web Vitals 或对 LCP 等方向进行非最终测量。

前端事务接续时刻不可靠

因为通盘的数据都必须在一个 transaction 中。Sentry 的浏览器 SDK 为每个页面加载和每个导航创建一个 transaction。这些 transaction 必须在某个时刻收尾。

若是在 transaction 完成之前关闭浏览器选项卡并将其发送到 Sentry,则通盘汇集的数据都会丢失。因此,SDK 需要均衡丢失所很是据的风险与汇集不齐备和可能不准确的数据的风险。

在洞悉到临了一个步履(举例传出的 HTTP 肯求)后自在了一段时刻后,Transaction 就完成了。这意味着页面加载或导航 transaction 的接续时刻是一个突出纵脱的值,不一定能改造或与其他事务比拟,因为它不成准确代表任何具体和可贯通的过程的接续时刻。

咱们通过将 LCP Web Vital 当作浏览器的默许性能方向来搪塞这一驱逐。但是,如上所述,LCP 值可能会在最终笃定之前发送,因此这不是逸想的科罚决策。

内存缓冲影响服务器

如前所述,面前的接管模子需要 Sentry SDK 来洞悉内存中的齐备 span 树。以恒定的并发 transaction 流运行的欺诈设施将需要多数的系统资源来汇集和处理追踪数据。Web 服务器是出现此问题的典型案例。

这意味着记载 100% 的 span 和 100% 的 transaction 关于许多服务器端欺诈设施来说是不可行的,因为所产生的支拨太高了。

无法批处理事务

Sentry 的接管模子不支援一次接管多个事件。突出是,SDK 不成将多个 transaction 批处理为一个肯求。

因此,当多笔 transaction 险些同期完成时,SDK 需要为每个 transaction 发出单独的肯求。这种行径在最佳的情况下瑕瑜常低效的,在最坏的情况下是对资源(如聚积带宽和CPU周期)的严重且有问题的消耗。

兼容性

Transaction Span 的特殊处理与 OpenTelemetry 不兼容。使用 OpenTelemetry SDK 检测现存欺诈设施的用户无法放松使用 Sentry 来获取和分析他们的数据。

Sentry 照实为 OpenTelemetry Collector 提供了一个 Sentry Exporter,但是,由于面前的接管模子,Sentry Exporter 有一个主要的正确性驱逐。

https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/sentryexporter#known-limitations

归来

通过在 Sentry 中构建面前的追踪杀青,咱们学到了好多。本文档试图捕捉许多已知的驱逐,以当作异日改造的基础。

追踪是一个复杂的主题,驯从这种复杂性并非易事。

第一组中的问题 - 与 scope propagation(作用域传播) 关系的问题 - 是 SDK 偏执想象神态专有的问题。科罚这些问题将需要对通盘 SDK 进行里面架构革新,包括从头想象面包屑等旧功能, 但进行此类革新是杀青浅薄易用的 tracing helper(如可在职何高下文中服务并拿获准确可靠的性能数据的 trace 函数)的先决要求。请重视,此类革新险些信服意味着发布新的主要 SDK 版块,这会禁闭与现存版块的兼容性。

第二组中的问题 - 与 span ingestion model(跨度接管模子) 关系的问题要复杂得多,因为为科罚这些问题所做的任何革新都会影响居品的更多部分,况兼需要多个团队的合营奋力。

 

尽管如斯,对 ingestion model 进行革新将对居品产生不可臆测的积极影响,因为这么做会升迁效果,使咱们或者汇集更多数据,并减少 instrumentation 的背负。

 







Powered by 福彩8 @2013-2022 RSS地图 HTML地图

Copyright 站群系统 © 2013-2021 365建站器 版权所有