Sentry(v20.12.1) K8S 云原生架构探索,JavaScript 性能监控之管理 Transactions-LMLPHP

系列

  1. Sentry-Go SDK 中文实践指南
  2. 一起来刷 Sentry For Go 官方文档之 Enriching Events
  3. Snuba:Sentry 新的搜索基础设施(基于 ClickHouse 之上)
  4. Sentry 10 K8S 云原生架构探索,Vue App 1 分钟快速接入
  5. Sentry(v20.12.1) K8S云原生架构探索,玩转前/后端监控与事件日志大数据分析,高性能高可用+可扩展可伸缩集群部署
  6. Sentry(v20.12.1) K8S 云原生架构探索,Sentry JavaScript SDK 三种安装加载方式
  7. Sentry(v20.12.1) K8S 云原生架构探索,SENTRY FOR JAVASCRIPT SDK 配置详解
  8. Sentry(v20.12.1) K8S 云原生架构探索, SENTRY FOR JAVASCRIPT 手动捕获事件基本用法
  9. Sentry(v20.12.1) K8S 云原生架构探索,SENTRY FOR JAVASCRIPT Source Maps 详解
  10. Sentry(v20.12.1) K8S 云原生架构探索,SENTRY FOR JAVASCRIPT 故障排除
  11. Sentry(v20.12.1) K8S 云原生架构探索,1分钟上手 JavaScript 性能监控

Automatic Instrumentation

要自动捕获 transactions,必须首先在应用程序中启用跟踪。

@sentry/tracing 包提供了一个 BrowserTracing 集成,以添加 automatic instrumentation 来监视浏览器应用程序的性能。

What Automatic Instrumentation Provides

BrowserTracing 集成为每个页面 load 和 navigation 事件创建一个新 transaction,并为在打开这些 transactions 时发生的每个 XMLHttpRequestfetch 请求创建一个 child span。进一步了解 traces, transactions, and spans。

Enable Automatic Instrumentation

要启用此自动跟踪,请在 SDK 配置选项中包含 BrowserTracing 集成。(请注意,使用 ESM 模块时,主要的 @sentry/* import 必须先于 @sentry/tracing import。)

配置完成后,在 sentry.io 中查看 transactions 时,您将同时看到 pageloadnavigation

ESM

// If you're using one of our integration packages, like `@sentry/react` or `@sentry/angular`,
// substitute its name for `@sentry/browser` here
import * as Sentry from "@sentry/browser";
import { Integrations as TracingIntegrations } from "@sentry/tracing"; // Must import second

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",

  integrations: [
    new Integrations.BrowserTracing({
      tracingOrigins: ["localhost", "my-site-url.com", /^\//],
      // ... other options
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

CDN

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",

  integrations: [
    new Sentry.Integrations.BrowserTracing({
      tracingOrigins: ["localhost", "my-site-url.com", /^\//],
      // ... other options
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

Configuration Options

您可以将许多不同的选项传递给 BrowserTracing 集成(作为 {optionName: value} 形式的对象),但是它具有合理的默认值。有关所有可能的选项,请参见 TypeDocs。

tracingOrigins

tracingOrigins 的默认值是 ['localhost', /^\//]。 JavaScript SDK 将 sentry-trace header 附加到其目标包含列表中的字符串或匹配列表中的正则表达式的所有传出的 XHR/fetch 请求。 如果您的前端向另一个域发出请求,则需要在其中添加它,以将 sentry-trace header 传播到后端服务,这是将 transactions 链接在一起作为单个跟踪的一部分所必需的。tracingOrigins 选项与整个请求 URL 匹配,而不仅仅是域。使用更严格的正则表达式来匹配 URL 的某些部分,可以确保请求不用不必要地附加 sentry-trace header。

例如:

  • 前端应用程序是从 example.com 提供的
  • 后端服务由 api.example.com 提供
  • 前端应用程序对后端进行 API 调用
  • 因此,该选项需要这样配置:new Integrations.BrowserTracing({tracingOrigins: ['api.example.com']})
  • 现在,向 api.example.com 发出的 XHR/fetch 请求将获得附加的 sentry-trace header
Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new Integrations.BrowserTracing({
      tracingOrigins: ["localhost", "my-site-url.com"],
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

您将需要配置您的 Web 服务器 CORS 以允许 sentry-trace header。该配置可能类似于 "Access-Control-Allow-Headers: sentry-trace",但是该配置取决于您的设置。如果您不允许使用 sentry-trace header,则该请求可能会被阻止。

beforeNavigate

对于 pageloadnavigation transactions,BrowserTracing 集成使用浏览器的 window.location API 生成 transaction 名称。要自定义 pageloadnavigation transactions 的名称,您可以向 BrowserTracing 集成提供 beforeNavigate 选项。该选项允许您修改 transaction 名称以使其更通用,例如,名为 GET /users/12312012GET /users/11212012 的 transactions 都可以重命名为 GET /users/:userid,以便他们可以在一起。

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new Integrations.BrowserTracing({
      beforeNavigate: context => {
        return {
          ...context,
          // You could use your UI's routing library to find the matching
          // route template here. We don't have one right now, so do some basic
          // parameter replacements.
          name: location.pathname
            .replace(/\d+/g, "<digits>")
            .replace(/[a-f0-9]{32}/g, "<hash>"),
        };
      },
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

shouldCreateSpanForRequest

此函数可用于过滤掉不需要的 spans,例如 XHR 的运行状况检查或类似的检查。 默认情况下,shouldCreateSpanForRequest 已经过滤掉了除了 tracingOrigins 中定义的内容以外的所有内容。

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new Integrations.BrowserTracing({
      shouldCreateSpanForRequest: url => {
        // Do not create spans for outgoing requests to a `/health/` endpoint
        return !url.match(/\/health\/?$/);
      },
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

Manual Instrumentation

要手动捕获 transactions,必须首先在应用程序中启用跟踪。

要手动 instrument 代码的某些区域,可以创建 transactions 来捕获它们。

这对于所有 JavaScript SDK(后端和前端)均有效,并且独立于 ExpressHttpBrowserTracing 集成而工作。

const transaction = Sentry.startTransaction({ name: "test-transaction" });
const span = transaction.startChild({ op: "functionX" }); // This function returns a Span
// functionCallX
span.finish(); // Remember that only finished spans will be sent with the transaction
transaction.finish(); // Finishing the transaction will send it to Sentry

例如,如果要为页面上的用户交互创建 transaction,请执行以下操作:

// Let's say this function is invoked when a user clicks on the checkout button of your shop
shopCheckout() {
  // This will create a new Transaction for you
  const transaction = Sentry.startTransaction('shopCheckout');
  // set the transaction on the scope so it picks up any errors
  hub.configureScope(scope => scope.setSpan(transaction));

  // Assume this function makes an xhr/fetch call
  const result = validateShoppingCartOnServer();

  const span = transaction.startChild({
    data: {
      result
    },
    op: 'task',
    description: `processing shopping cart result`,
  });
  processAndValidateShoppingCart(result);
  span.finish();

  transaction.finish();
}

这个例子将发送一个 transaction shopCheckout 到 Sentry。 交易将包含一个 task span,该 span 衡量 processAndValidateShoppingCart 花费了多长时间。最后,对 transaction.finish() 的调用将完成transaction 并将其发送给 Sentry。

在为异步操作创建 spans 时,您还可以利用 Promises。但是请记住,必须在调用 transaction.finish() 之前将其 span 包含在事务中。

例如:

function processItem(item, transaction) {
  const span = transaction.startChild({
    op: "http",
    description: `GET /items/:item-id`,
  });

  return new Promise((resolve, reject) => {
    http.get(`/items/${item.id}`, response => {
      response.on("data", () => {});
      response.on("end", () => {
        span.setTag("http.status_code", response.statusCode);
        span.setData("http.foobarsessionid", getFoobarSessionid(response));
        span.finish();
        resolve(response);
      });
    });
  });
}

Connect Backend and Frontend Transactions

要将后端和前端 transactions 连接到单个一致的跟踪中,Sentry 使用 trace_id 值,该值在前端和后端之间传播。根据情况,此 ID 可以在请求 header 或 HTML <meta> 标记中传输。以这种方式链接 transactions 使您可以在 Sentry UI 中在它们之间进行导航,因此您可以更好地了解系统的不同部分如何相互影响。您可以在我们的分布式跟踪文档中了解有关此模型的更多信息。

Pageload

在前端和后端都启用跟踪并利用自动前端 instrumentation 功能时,可以将前端上自动生成的 pageload transaction 与后端上的为页面服务提供请求的 transaction 相连接。因为在浏览器中运行的 JavaScript 代码无法读取当前页面的响应 headers,所以 trace_id 必须在响应本身中传输,尤其是在从后端发送的 HTML <head> 中的 <meta> 标签中。

<html>
  <head>
    <meta name="sentry-trace" content="{{ span.toTraceparent() }}" />
    <!-- ... -->
  </head>
</html>

name 属性必须是字符串 "sentry-trace"content 属性必须由后端的 Sentry SDK 使用 span.toTraceparent()(或等效项,取决于后端平台)生成。这保证了将为每个请求生成一个新的唯一值。

span 引用是为 HTML 提供服务的 transaction,或其任何 child spans。 它定义了 pageload transaction 的父级。

一旦数据被包含在 <meta> 标签中,我们的 BrowserTracing 集成将自动获取数据并将其链接到在 pageload 时生成的 transaction。(请注意,它不会链接到自动生成的 navigation transactions,即不需要重新加载整个页面的 transaction。每个 transaction 都是后端不同请求 transaction 的结果,因此应具有唯一的 trace_id。)

Navigation and Other XHR Requests

加载页面后,它发出的任何请求(以及后端产生的任何请求)都通过请求 header 链接。

就像上面讨论的 <meta> 标签一样,标题的名称是 sentry-trace,其值是通过调用 span.toTraceparent()(或等效的)来获得的,其中 span 是相关 transaction 或其任何子项。

Sentry 的所有与跟踪相关的集成(BrowserTracingHttpExpress)都会针对它们生成的所有 transactions 和 spans 自动生成或拾取并传播此 header。在手动创建 transaction 或 span 的任何情况下,您都可以自己附加和读取 header,这样做很有意义。

Control Data Truncation

当前,每个标签的最大字符数限制为200个字符。超过200个字符限制的标签将被截断,丢失潜在的重要信息。要保留此数据,您可以将数据拆分为多个标签。

例如,一个200多个字符标记的请求:

https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=161803398874989484820458683436563811772030917980576

上面200个字符以上的请求将被截断为:

https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=1618033988749894848

相反,使用 span.set_tagspan.set_data 会使用结构化元数据保留此查询的详细信息。这可以通过 baseUrlendpointparameters 完成:

const baseUrl = "https://empowerplant.io";
const endpoint = "/api/0/projects/ep/setup_form";
const parameters = {
  user_id: 314159265358979323846264338327,
  tracking_id: "EasyAsABC123OrSimpleAsDoReMi",
  product_name: PlantToHumanTranslator,
  product_id: 161803398874989484820458683436563811772030917980576,
};

const span = transaction.startChild({
  op: "request",
  description: "setup form",
});

span.setTag("baseUrl", baseUrl);
span.setTag("endpoint", endpoint);
span.setData("parameters", parameters);
// you may also find some parameters to be valuable as tags
span.setData("user_id", parameters.user_id);
http.get(`${base_url}/${endpoint}/`, (data = parameters));

Group Transactions

Sentry 捕获 transactions 时,将为它们分配一个 transaction 名称。该名称通常由 Sentry SDK 根据您使用的框架集成自动生成。如果您无法利用自动 transaction 生成(或想要自定义 transaction 名称的生成方式),则可以使用,在使用配置初始化 SDK 时注册的全局事件处理器。

在 node.js 应用程序中执行此操作的示例:

import { addGlobalEventProcessor } from "@sentry/node";

addGlobalEventProcessor(event => {
  // if event is a transaction event
  if (event.type === "transaction") {
    event.transaction = sanitizeTransactionName(event.transaction);
  }
  return event;
});

对于使用 BrowserTracing 集成的浏览器 JavaScript 应用程序,beforeNavigate 选项可用于根据 URL 更好地将 navigation/pageload transactions 分组在一起。

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  // ...
  integrations: [
    new Integrations.BrowserTracing({
      beforeNavigate: context => {
        return {
          ...context,
          // You could use your UI's routing library to find the matching
          // route template here. We don't have one right now, so do some basic
          // parameter replacements.
          name: location.pathname
            .replace(/\d+/g, "<digits>")
            .replace(/[a-f0-9]{32}/g, "<hash>"),
        };
      },
    }),
  ],
});

Retrieve an Active Transaction

如果要将 Spans 附加到已在进行中的 transaction 中,例如在对 transaction 进行分组时,可以使用 Sentry.getCurrentHub().getScope().getTransaction()。当 scope 中有正在运行的 transaction 时,此函数将返回一个 Transaction 对象,否则它将返回 undefined。如果您使用的是 BrowserTracing 集成,则默认情况下,我们会将 transaction 附加到 Scope,因此您可以执行以下操作:

function myJsFunction() {
  const transaction = Sentry.getCurrentHub()
    .getScope()
    .getTransaction();
  if (transaction) {
    let span = transaction.startChild({
      op: "encode",
      description: "parseAvatarImages",
    });
    // Do something
    span.finish();
  }
}

中文文档陆续同步到:

我是为少。
微信:uuhells123。
公众号:黑客下午茶。
谢谢点赞支持👍👍👍!
01-22 04:11