新的追踪 API
了解 Sentry JavaScript SDK 8.x 中的新追踪 API
SDK 8.x
版本引入了新的性能监控 API。这些 API 旨在提供对如何收集和报告性能数据的更多控制。
这些 API 在 7.x
中也已提供,因此您可以在升级到 8.x
之前逐步迁移到新 API。
以前,有两个关键 API 用于为应用程序添加手动性能监控:
startTransaction()
span.startChild()
这些 API 基于 Sentry 的原始数据模型。该模型基于一个根事务(Transaction),它可以包含一个嵌套的 Span 树。
在新模型中,事务的概念已经消失。现在,所有操作都是在 span 上进行的,无论它们在树中的位置如何。请注意,span 仍然可以在 Sentry UI 中被分组为一个事务。但是,这在后台发生,从 SDK 的角度来看,您只需要关注 span。
新模型在手动启动或结束时不再区分事务和 span。相反,您始终使用相同的 API 来启动一个新的 span。这将根据当前活动的 span 自动创建一个新的根 Span(没有父级的常规 span,概念上类似于事务)或子 Span。
有三个关键 API 可用于启动 span:
startSpan()
startSpanManual()
startInactiveSpan()
所有三个 span API 都接受 StartSpanOptions
作为第一个参数,其结构如下:
interface StartSpanOptions {
// The only required field - the name of the span
name: string;
attributes?: SpanAttributes;
op?: string;
scope?: Scope;
forceTransaction?: boolean;
}
这是在大多数情况下应使用的最常见的 API。它将启动一个新的 span,在给定回调的持续时间内使其成为活动 span,并在回调结束时自动结束该 span。您可以这样使用它:
Sentry.startSpan(
{
name: "my-span",
attributes: {
attr1: "my-attribute",
attr2: 123,
},
},
(span) => {
// do something that you want to measure
// once this is done, the span is automatically ended
}
);
您也可以传递一个异步函数:
Sentry.startSpan(
{
name: "my-span",
attributes: {},
},
async (span) => {
// do something that you want to measure
await waitOnSomething();
// once this is done, the span is automatically ended
}
);
由于 startSpan()
会将创建的 span 设置为活动 span,任何在回调中创建 span 的自动或手动 instrumentation 都会将新 span 作为我们刚刚启动的 span 的子项附加。
请注意,如果在回调中抛出错误,span 状态将自动设置为错误状态。
这是 startSpan()
的一个变体,唯一的区别是它不会在回调结束时自动结束 span。使用此方法时,您需要自己调用 span.end()
:
Sentry.startSpanManual(
{
name: "my-span",
},
(span) => {
// do something that you want to measure
// Now manually end the span ourselves
span.end();
}
);
在大多数情况下,startSpan()
应该能满足您手动 instrumentation 的所有需求。但如果您发现自己处于某种情况下,自动结束 span 无法满足您的需求,您可以改用 startSpanManual()
。
此函数也会将创建的 span 设置为回调期间的活动 span,并且如果回调内抛出错误,它也会将 span 状态更新为错误状态。
与另外两种方法不同,此方法不接受回调,也不会将创建的 span 设置为活动 span。如果您想创建不需要有任何子项的独立 span,可以使用此方法:
Sentry.startSpan({ name: "outer" }, () => {
const inner1 = Sentry.startInactiveSpan({ name: "inner1" });
const inner2 = Sentry.startInactiveSpan({ name: "inner2" });
// do something
// manually end the spans
inner1.end();
inner2.end();
});
不会有任何 span 作为非活动 span 的子 span 创建。
您可以使用 withActiveSpan
辅助函数来创建作为特定 span 子项的 span:
Sentry.withActiveSpan(parentSpan, () => {
Sentry.startSpan({ name: "my-span" }, (span) => {
// span will be a direct child of parentSpan
});
});
虽然在大多数情况下,您不需要区分创建 span 还是事务(只需调用 startSpan()
,我们会自动处理),但有时您可能需要确保创建一个事务(例如,如果您需要在 Sentry UI 中将其视为事务)。对于这些情况,您可以传递 forceTransaction: true
到启动 span 的 API,例如:
const transaction = Sentry.startInactiveSpan({
name: "transaction",
forceTransaction: true,
});
以前,span 和事务有许多属性和方法可以使用。大多数这些属性和方法已被移除,以支持更简洁、更直接的 API,该 API 也与 OpenTelemetry Spans 对齐。您可以参考下表,了解以前存在的内容以及如何映射到新的 API:
spanToJSON
、getRootSpan
、setHttpStatus
、spanToTraceHeader
和 spanToTraceContext
均由 SDK 导出以供使用。
旧名称 | 替换为 |
---|---|
span.traceId | span.spanContext().traceId |
span.spanId | span.spanContext().spanId |
span.parentSpanId | spanToJSON(span).parent_span_id |
span.status | spanToJSON(span).status |
span.sampled | spanIsSampled(span) |
span.startTimestamp | span.startTime - 注意格式不同! |
span.tags | 使用属性或在作用域中设置标签 |
span.data | spanToJSON(span).data |
span.transaction | getRootSpan(span) |
span.instrumenter | 已移除 |
span.finish() | span.end() |
span.end() | 相同 |
span.setTag() | span.setAttribute() 或在作用域中设置标签 |
span.setData() | span.setAttribute() |
span.setStatus() | span.setStatus - 注意签名不同 |
span.setHttpStatus() | setHttpStatus(span, status) |
span.setName() | span.updateName() |
span.startChild() | 独立调用 Sentry.startSpan() |
span.isSuccess() | spanToJSON(span).status === 'ok' |
span.toTraceparent() | spanToTraceHeader(span) |
span.toContext() | 已移除 |
span.updateWithContext() | 已移除 |
span.getTraceContext() | spanToTraceContext(span) |
此外,事务有以下 API:
旧名称 | 替换为 |
---|---|
name | spanToJSON(span).description |
trimEnd | 已移除 |
parentSampled | spanIsSampled(span) & spanContext().isRemote |
metadata | 使用属性代替或在作用域中设置 |
setContext() | 在作用域中设置上下文 |
setMeasurement() | Sentry.setMeasurement() |
setMetadata() | 使用属性代替或在作用域中设置 |
getDynamicSamplingContext | getDynamicSamplingContextFromSpan(span) |
以前的模型中有 Data、Tags 和 Context 的概念,它们可以用于不同的目的。然而,这有两个主要缺点:首先,在给定情况下并不总是清楚应该使用哪个概念。其次,这些概念在事务或 span 中的显示方式不同。
为了解决这些问题,新模型只保留了可以在 span 上设置的 Attributes。大致来说,它们对应于以前的 Data。
如果您仍然需要设置标签或上下文,可以
Sentry.withScope((scope) => {
scope.setTag("my-tag", "tag-value");
Sentry.startSpan({ name: "my-span" }, (span) => {
// do something here
// span will have the tags from the containing scope
});
});
除了整体更改性能 API 之外,还有一些较小的变更。
目前,tracesSampler()
可以接收作为参数传递的任意 SamplingContext
。虽然这个上下文的具体结构未在任何地方详细定义,但在 v8 中其结构将发生变化。从现在起,它主要会接收 span 的属性以及一些其他相关数据。像 req
(用于基于 Node 的 SDK)或 location
(用于浏览器追踪)这样的属性,以前有时会传递,但以后将不再传递。
在 v7 中,如果禁用了追踪或 span 未被采样,性能 API startSpan()
/ startInactiveSpan()
/ startSpanManual()
接收到的 span 可能是 undefined
。
在 v8 中,为了与 OpenTelemetry 对齐,这些方法将始终返回一个 span——但该 span 可能是一个 Noop-Span,即永远不会发送的 span。这意味着您不再需要在代码的每个地方检查 span 是否存在:
Sentry.startSpan((span: Span | undefined) => {
// previously, in order to be type safe, you had to use optional chaining or similar
span?.setAttribute("attr", 1);
});
// In v8, the signature changes to:
Sentry.startSpan((span: Span) => {
// no need to guard anymore!
span.setAttribute("attr", 1);
});
在 v7 中,您可以通过在调用 startTransaction
时设置 sampled
选项来强制执行正面或负面的采样决策。这将有效地覆盖特定事务的 采样配置。
在 v8 中,为了与 OpenTelemetry 对齐,sampled
选项已被移除。您仍然可以通过在 Sentry.init
中定义 tracesSampler
回调,并为特定 span 返回 1
或 0
来强制决策:
// v7
Sentry.startTransition({op: 'function.myFunction', sampled: true});
// v8
// 1. define a tracesSampler
Sentry.init({
tracesSampler: (samplingContext) => {
// force a positive sampling decision for a specific span
if (samplingContext.op === 'function.myFunction') {
return 1;
}
// force a negative sampling decision for a specific span
if (samplingContext.op === 'function.healthCheck') {
return 0;
}
// return 0.1 as a default sample rate for all other spans
return 0.1;
}
});
// 2. start the span
Sentry.startSpan({op: 'function.myFunction'}, {/*...*/});