自定义插桩

了解如何捕获应用程序中任何操作的性能数据。

要捕获符合您组织需求的自定义事务和跨度,您必须首先 设置追踪。

要为您的应用程序添加自定义性能数据,您需要以跨度的形式添加自定义插桩。跨度是一种测量特定操作所需时间的方法。例如,您可以创建一个跨度来测量函数执行所需的时间。

要开始,请导入 SDK。

Copied
const Sentry = require("@sentry/node");

创建跨度有三个关键函数:

  • startSpan:创建一个新的活动跨度,并自动结束。您可能希望使用此函数。
  • startSpanManual:创建一个新的活动跨度,但需要手动结束。
  • startInactiveSpan:创建一个新的非活动跨度,需要手动结束。

当启动一个新的跨度时,如果当前有活动的父跨度,它将自动作为该父跨度的子跨度启动。这意味着,如果一个跨度被启动为 活动跨度,在该跨度活跃期间创建的任何其他跨度将成为它的子跨度。此外,错误将与当前活动的跨度关联(如果有)。

相比之下,非活动跨度 不会自动关联任何子跨度。这在您不关心捕获子活动的情况下非常有用。

活动跨度的一个关键约束是它们只能在回调函数中激活。这个约束存在的原因是,在处理异步代码时,否则无法将跨度正确地关联到父跨度。

在无法将执行代码包装在回调函数中的地方(例如,使用钩子或其他类似情况),您需要使用非活动跨度,并可以结合 withActiveSpan 手动将子跨度与正确的父跨度关联。

以下选项可以用于所有启动跨度的函数:

选项类型描述
namestring跨度的名称。
opstring跨度的操作类型。
startTimenumber跨度的开始时间。
attributesRecord<string, Primitive>要附加到跨度的属性。
parentSpanSpan如果设置,将使该跨度成为指定跨度的子跨度。否则,该跨度将成为当前活动跨度的子跨度。
onlyIfParentboolean如果为 true,在没有活动父跨度时忽略该跨度。
forceTransactionboolean如果为 true,确保此跨度在 Sentry UI 中显示为事务。

只有 name 是必需的,其他所有选项都是可选的。

对于大多数场景,我们建议使用 Sentry.startSpan() 启动活动跨度。这将启动一个新的活动跨度,在提供的回调函数中激活,并在回调函数完成后自动结束该跨度。回调函数可以是同步的,也可以是异步的(返回一个 Promise)。

为同步操作启动一个跨度:

Copied
const result = Sentry.startSpan({ name: "Important Function" }, () => {
  return expensiveFunction();
});

为异步操作启动一个跨度:

Copied
const result = await Sentry.startSpan(
  { name: "Important Function" },
  async () => {
    const res = await doSomethingAsync();
    return updateRes(res);
  },
);

您还可以嵌套跨度:

Copied
const result = await Sentry.startSpan(
  {
    name: "Important Function",
  },
  async () => {
    const res = await Sentry.startSpan({ name: "Child Span" }, () => {
      return expensiveAsyncFunction();
    });

    return updateRes(res);
  },
);

有时,您不希望在回调函数完成后自动结束跨度。在这种情况下,您可以使用 Sentry.startSpanManual()。这将启动一个新的活动跨度,在提供的回调函数中激活,但不会在回调函数完成后自动结束。您需要通过调用 span.end() 手动结束该跨度。

Copied
// Start a span that tracks the duration of middleware
function middleware(_req, res, next) {
  return Sentry.startSpanManual({ name: "middleware" }, (span) => {
    res.once("finish", () => {
      span.setHttpStatus(res.status);
      // manually tell the span when to end
      span.end();
    });
    return next();
  });
}

要添加非活动的跨度,您可以创建独立的跨度。这在您有多个工作项归类于单个父跨度下,但与当前活动跨度无关时非常有用。然而,在大多数情况下,您会希望使用上述的 startSpan API。

Copied
const span1 = Sentry.startInactiveSpan({ name: "span1" });

someWork();

span1.end();

默认情况下,启动的任何跨度都会成为当前活动跨度的子跨度。如果您希望有不同的行为,可以使用 parentSpan 选项强制跨度成为特定跨度的子跨度:

Copied
const parentSpan = Sentry.startInactiveSpan({ name: "Parent Span" });
const childSpan = Sentry.startInactiveSpan({ name: "Child Span", parentSpan });

childSpan.end();
parentSpan.end();

此选项也适用于 startSpanstartSpanManual

我们提供了一些有用的工具,可以帮助您进行自定义插桩。

返回当前活动的跨度。

Copied
const activeSpan = Sentry.getActiveSpan();

返回给定跨度的根跨度。如果该跨度已经是根跨度,则返回该跨度本身。

Copied
const activeSpan = Sentry.getActiveSpan();
const rootSpan = activeSpan ? Sentry.getRootSpan(activeSpan) : undefined;

此方法允许您在回调函数的执行期间使一个跨度成为活动状态。您可以将其与 startInactiveSpan 结合使用,以手动将子跨度与正确的父跨度关联:

Copied
const span = Sentry.startInactiveSpan({ name: "Parent Span" });

Sentry.withActiveSpan(span, () => {
  // `span` is now active, any other spans will be children of it
  Sentry.startSpan({ name: "Child Span" }, () => {
    // Do something
  });
});

您还可以将 null 传递给 withActiveSpan,以确保该跨度没有父跨度:

Copied
Sentry.withActiveSpan(null, () => {
  // This will not have a parent span, no matter what
  Sentry.startSpan({ name: "Parent Span" }, () => {
    // Do something
  });
});

或者,您也可以使用 parentSpan 选项来达到相同的效果:

Copied
const span = Sentry.startInactiveSpan({ name: "Parent Span" });
const childSpan = Sentry.startInactiveSpan({
  name: "Child Span",
  parentSpan: span,
});

在回调函数执行期间抑制采样跨度的创建。这在您希望阻止某些跨度被捕获时非常有用。例如,如果您不希望为某个 fetch 请求创建跨度,可以这样做:

Copied
Sentry.suppressTracing(() => {
  fetch("https://example.com");
});

有关如何手动设置分布式追踪的详细信息,请参阅 分布式追踪

您可以在创建跨度时捕获跨度属性。跨度属性可以是 stringnumberboolean 类型,也可以是这些类型的(非混合)数组。您可以在启动跨度时指定属性:

Copied
Sentry.startSpan(
  {
    attributes: {
      attr1: "value1",
      attr2: 42,
      attr3: true,
    },
  },
  () => {
    // Do something
  },
);

或者您也可以向现有的跨度添加属性:

Copied
const span = Sentry.getActiveSpan();
if (span) {
  span.setAttribute("attr1", "value1");
  // Or set multiple attributes at once:
  span.setAttributes({
    attr2: 42,
    attr3: true,
  });
}

跨度可以关联一个操作,这有助于 Sentry 识别关于该跨度的额外上下文。例如,与数据库相关的跨度通常关联 db 操作。Sentry 产品为具有已知操作的跨度提供了额外的控制、可视化和过滤功能。

Sentry 维护了一个 常见跨度操作列表,建议您在适用时使用这些操作之一。

Copied
const result = Sentry.startSpan({ name: 'SELECT * FROM TABLE', op: 'db.query' }, () => {
  return execQuery();
})

自 v8.47.0 可用

您可以在任何时候更新跨度的名称:

Copied
const span = Sentry.getActiveSpan();
if (span) {
  Sentry.updateSpanName(span, "New Name");
}

在 v8.39.0 之前,您必须使用 span.updateName('New Name'),这在 @sentry/node 及依赖它的 SDK(例如 @sentry/nextjs)中有一些限制:

  • 具有 http.methodhttp.request.method 属性的跨度会自动将其名称设置为方法 + URL 路径。
  • 具有 db.system 属性的跨度会自动将其名称设置为系统 + 语句。

使用 Sentry.updateSpanName() 可确保名称正确更新,并且在这些情况下不再被覆盖。

如果您在浏览器环境中使用 @sentry/browser@sentry/react 等,span.updateName()Sentry.updateSpanName() 的功能是相同的,因此您可以使用其中任何一个。