sentry性能监控之入门(翻译)

5,969 阅读5分钟

原文: docs.sentry.io/product/per…

在这个快速设置之后,您将可以访问性能。这个工具将使您能够查看从宏观层面的指标到微观层面的跨度的所有信息,但是您将能够交叉引用相关问题的事务,根据您的个人需求定制查询,等等。

Sentry的性能特性现在可以使用了。我们任何一个遗留计划的客户都需要在其订阅中添加事务事件以访问性能。有关访问这些功能的更多详细信息,请访问performance-feedback@sentry.io

支持用于跟踪的SDK

  • JavaScript浏览器SDK≥5.16.0

  • JavaScript nodeSDK≥5.16.0

  • Python SDK版本≥0.11.2

  • Javascript React SDK≥5.18.1

安装和配置SDK

要开始使用Sentry的JavaScript SDK进行性能监视,请首先安装@Sentry/browser和@Sentry/apm包:

# Using yarn
$ yarn add @sentry/browser @sentry/apm

# Using npm
$ npm install @sentry/browser @sentry/apm

接下来,在Sentry.init初始化集成:

import * as Sentry from '@sentry/browser';
import { Integrations as ApmIntegrations } from '@sentry/apm';
Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  release: 'my-project-name@' + process.env.npm_package_version,
  integrations: [
    new ApmIntegrations.Tracing(),
  ],
  tracesSampleRate: 1.0, // Be sure to lower this in production
});

或者,您可以使用我们预先构建的CDN捆绑包来代替npm包,它结合了@sentry/browser和@sentry/apm:

	<script src="https://browser.sentry-cdn.com/5.21.1/bundle.apm.min.js" integrity="sha384-HCyOLJ09LSmwmNm/rjzAjA265g43guENxt7Z+wKmtIxj3xD6XEu1d0vwMSTTZ/UF" crossorigin="anonymous"></script>	

接下来,在Sentry.init初始化集成:

 Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  integrations: [
    new Sentry.Integrations.Tracing(),
  ],
  tracesSampleRate: 1.0, // Be sure to lower this in production
});

性能数据使用名为“事务”的新事件类型进行传输,您可以在分布式跟踪中了解该类型。要捕获事务,必须安装performance包并配置SDK以将tracesSampleRate配置设置为非零值。上面的示例配置将100%传输捕获的事务。一定要在生产中降低这个值,否则你会很快耗尽你的配额。

使用SDK筛选事件中了解有关采样的详细信息。

自动仪表

对于@sentry/browser,我们提供了一个名为Tracing的集成,它在浏览器中执行自动检测。跟踪集成创建页面加载和导航事务,其中包含XHR/fetch请求的跨度和性能API条目,如标记、度量和资源计时。

跟踪集成特定于@sentry/browser,不适用于@sentry/node。

跟踪集成位于@sentry/apm包中。你可以把它加到你的Sentry.init:

// Without CDN

import * as Sentry from '@sentry/browser';
import { Integrations as ApmIntegrations } from '@sentry/apm';

Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  integrations: [
    new ApmIntegrations.Tracing(),
  ],
  tracesSampleRate: 1.0, // Be sure to lower this in production
});

// With CDN

Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  integrations: [
    new Sentry.Integrations.Tracing(),
  ],
  tracesSampleRate: 1.0, // Be sure to lower this in production
});

注意:跟踪集成在Sentry.Integrations.Tracing 当使用CDN捆绑包时。

要发送跟踪,需要将tracesSampleRate设置为非零值。上面的配置将捕获100%的事务。

默认情况下,pageload和navigation事务使用window.location.pathname.

您可以向跟踪集成传递许多不同的选项(作为{optionName:value}形式的对象),但它提供了开箱即用的合理默认值。

有关所有可能的选项,请参见TypeDocs

tracingOrigins选项

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

例子:

  • 前端应用程序从example.com网站

  • 后端服务从api.example.com网站

  • 前端应用程序对后端进行API调用

  • 因此,需要将该选项配置为:new ApmIntegrations.Tracing({tracingOrigins: ['api.example.com']})

  • 现在传出XHR/fetch请求到api.example.com网站会得到sentry-trace的头

注意:您需要确保您的web服务器CORS配置为允许sentry跟踪头。配置可能看起来像Access-Control-Allow-Headers: sentry-trace,但这在很大程度上取决于您的设置。如果不允许sentry跟踪头,则请求可能会被阻止。

导航前选项

对于页面加载和导航事务,跟踪集成使用浏览器的窗口位置生成事务名称的API。要自定义pageload和导航事务的名称,可以向跟踪集成提供beforeNavigation选项。此选项允许您传入一个函数,该函数接受导航时的位置并返回新的事务名称。

如果希望利用来自自定义路由库(如React Router)的路由,或者希望减少特定事务的基数,则beforeNavigation非常有用。

import * as Sentry from '@sentry/browser';
import { Integrations as ApmIntegrations } from '@sentry/apm';
Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  integrations: [
    new ApmIntegrations.Tracing({
      beforeNavigate: location => {
        // Here we are doing some basic parameter replacements, but we recommend
        // using your UI's routing library to find the matching route template here
        return location.pathname
          .replace(/\d+/g, '<digits>')
          .replace(/[a-f0-9]{32}/g, '<hash>');
      },
    }),
  ],
  tracesSampleRate: 1.0, // Be sure to lower this in production
});

手动仪表

要手动检测代码的某些区域,可以创建事务来捕获它们。这对JavaScript浏览器和node都有效,并且独立于跟踪集成工作。

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	

这是一个不同的例子。如果要为页面上的用户交互创建事务,则需要执行以下操作:

// 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');

  // 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();
}

这个例子将向Sentry发送一个事务shopCheckout。事务将包含一个任务范围,用于度量processAndValidateShoppingCart所用的时间。最后,给事务处理完成()将完成交易并发送给Sentry。

向事务添加其他跨距

下一个示例包含从上一节中的代码片段调用的假设processItem函数的实现。我们的SDK可以确定当前是否有打开的事务,并将所有新创建的跨域作为子操作添加到其中。请记住,每个单独的范围都需要手动完成;否则,该范围将不会显示在事务中。

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

  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);
      });
    });
  });
}

分组事务记录

当Sentry捕获事务时,会为它们分配一个事务名称。此名称通常由Sentry SDK根据您使用的框架集成自动生成。如果您不能利用自动事务生成(或希望自定义事务名的生成方式),则可以使用全局事件处理器,该处理器是在使用配置初始化SDK时注册的。

一个在node.js应用:

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

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

对于使用跟踪集成的浏览器JavaScript应用程序,beforeNavigate选项可用于根据URL将导航/页面加载事务更好地分组在一起。

 import * as Sentry from '@sentry/browser';
import { Integrations as ApmIntegrations } from '@sentry/apm';

Sentry.init({
    // ...
  integrations: [
    new ApmIntegrations.Tracing({
      beforeNavigate: (location) => {
        // 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.
        return location.pathname
        .replace(/\d+/g, '<digits>')
        .replace(/[a-f0-9]{32}/g, '<hash>');
      },
    }),
  ],
});

检索事务

如果要将范围附加到已在进行的事务,则可以使用Sentry.getCurrentHub().getScope().getTransaction()。如果有正在运行的事务,此函数将返回一个事务,否则返回未定义的事务。如果您在默认情况下使用我们的跟踪集成,我们会将事务附加到作用域。所以你可以这样做:

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

向跨距添加查询信息和参数

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

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

https://enpowerplant.io/api/0/projects/ep/setup_form/?用户编号=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=1618033988874989484820458683436563811772030917980576

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

https://enpowerplant.io/api/0/projects/ep/setup_form/?用户_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=161803988749894848

相反,使用span.set_标记以及span.set_数据使用结构化元数据保留此查询的详细信息。这可以通过baseUrl、端点和参数完成:

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);