mcpField
S
sentry-span-streaming-dart

sentry-span-streaming-dart

github.com/getsentry开发272211 次安装今天更新
代码仓库

关于 sentry-span-streaming-dart

为 Dart 和 Flutter 项目提供 Sentry Span Streaming 迁移指导的技能,面向需要切换追踪生命周期的开发者。它涵盖版本与现有用法检查、API 和回调迁移,以及静态和运行时验证。

主要功能

  1. 01检测 Sentry SDK 版本与追踪用法
  2. 02配置流式 Span 生命周期
  3. 03将事务 API 迁移到 Span API
  4. 04迁移属性、回调与忽略规则
  5. 05提供静态和运行时验证步骤

适用场景

  1. 01在 Dart 项目启用 Span Streaming
  2. 02迁移 Flutter 项目的事务追踪
  3. 03处理旧 API 与回调的兼容调整
  4. 04验证迁移后的追踪数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/getsentry/sentry-for-ai --skill sentry-span-streaming-dart

安装后在 Claude Code 中输入/sentry-span-streaming-dart确认已加载。

详细介绍

所有技能 > 功能设置 > Span 流式传输(Dart)

Sentry Span 流式传输迁移(Dart/Flutter)

从默认的基于事务的跟踪生命周期(SentryTraceLifecycle.static)迁移到 Span 流式传输(SentryTraceLifecycle.stream)。在此模式下,Span 会在完成时逐个发送,而不是在最后批量打包到一个事务中。

本技能适用于 Dart 和 Flutter SDK(sentry、sentry_flutter,以及 sentry_dio、sentry_sqflite、sentry_drift 等集成包)。JavaScript 请参阅 Span 流式传输(JavaScript)。Python 请参阅 Span 流式传输(Python)。

在以下情况下调用此技能

  • 用户要求在 Dart 或 Flutter 项目中“启用 Span 流式传输”或“迁移到 Span 流式传输”
  • 用户想从基于事务的 Span 发送方式切换为流式发送
  • 用户提到 traceLifecycle、SentryTraceLifecycle.stream、Sentry.startSpan 或 SentrySpanV2
  • 用户希望降低 Span 发送延迟或逐个处理 Span

阶段 1:检测

1.1 检测 SDK 和版本
# Which Sentry packages does the project use?
grep -n "sentry" pubspec.yaml 2>/dev/null

# Resolved versions — span streaming requires >= 9.19.0
grep -A8 '^  sentry:' pubspec.lock 2>/dev/null | grep -m1 version
grep -A8 '^  sentry_flutter:' pubspec.lock 2>/dev/null | grep -m1 version

Span 流式传输要求 sentry / sentry_flutter 版本为 >=9.19.0。

1.2 查找现有的 Sentry 配置和跟踪用法
# Find init calls
grep -rn "SentryFlutter\.init\|Sentry\.init" lib/ bin/ test/ --include="*.dart" 2>/dev/null | head -10

# Find old transaction API usage (these become no-ops in streaming mode)
grep -rn "startTransaction\|\.startChild(" lib/ bin/ test/ --include="*.dart" 2>/dev/null | head -20

# Find callbacks affected by the migration
grep -rn "beforeSendTransaction\|beforeSendSpan\|ignoreSpans" lib/ bin/ test/ --include="*.dart" 2>/dev/null

# Find untyped span data/tag usage on transactions
grep -rn "\.setData(\|\.setTag(" lib/ bin/ test/ --include="*.dart" 2>/dev/null | head -20
1.3 确定迁移工作
发现迁移工作
Sentry.startTransaction(...) 调用替换为 Sentry.startSpan / Sentry.startSpanSync / Sentry.startInactiveSpan
span.startChild(...) 调用替换为嵌套调用 Sentry.startSpan——通过 zones 自动建立父子关系
对 Span 调用 setData / setTag替换为类型化的 span.setAttribute(...) / SentryAttribute
beforeSendTransaction 回调流式模式下不会调用——将逻辑迁移到 beforeSendSpan 或 ignoreSpans
已使用 beforeSendSpan / ignoreSpans根据流式语义进行检查——beforeSendSpan 仅用于修改,ignoreSpans 仅匹配名称(见 2.5/2.6)
仅使用自动检测(没有手动 Span)只需启用该选项——集成会自动切换

阶段 2:迁移

前提条件: sentry / sentry_flutter 版本为 >=9.19.0,且已启用跟踪(配置了 tracesSampleRate 或 tracesSampler)。

2.1 启用 Span 流式传输

在初始化选项中设置 traceLifecycle。这是唯一必需的配置更改。

// Flutter
await SentryFlutter.init((options) {
  options.dsn = '__DSN__';
  options.tracesSampleRate = 1.0;
  options.traceLifecycle = SentryTraceLifecycle.stream;
}, appRunner: () => runApp(const MyApp()));

// Plain Dart
await Sentry.init((options) {
  options.dsn = '__DSN__';
  options.tracesSampleRate = 1.0;
  options.traceLifecycle = SentryTraceLifecycle.stream;
});

一次只能使用一种跟踪系统:

  • 在 stream 模式下,事务 API(Sentry.startTransaction、ISentrySpan.startChild)不起作用,并会记录警告。
  • 在 static 模式(默认模式)下,新的 Span API(Sentry.startSpan)不起作用。
  • 自动检测会根据此设置自动切换到正确的 API。
2.2 将事务替换为 Span

Sentry.startSpan 会运行异步回调,并在返回的 future 完成时结束 Span。嵌套调用会通过 zones 自动建立父子关系——无需迁移到 startChild 的对应 API,只需进行嵌套调用即可。

// Before (static mode)
final transaction = Sentry.startTransaction('checkout', 'task');
try {
  final child = transaction.startChild('db.query', description: 'load cart');
  final cart = await loadCart();
  await child.finish();
  transaction.setData('cart.item_count', cart.items.length);
  transaction.status = const SpanStatus.ok();
} catch (exception) {
  transaction.status = const SpanStatus.internalError(); // -> span.status (see below)
  rethrow;
} finally {
  await transaction.finish();
}

// After (streaming mode)
await Sentry.startSpan('checkout', (span) async {
  final cart = await Sentry.startSpan('load cart', (_) => loadCart());
  span.setAttribute('cart.item_count', SentryAttribute.int(cart.items.length));
});

错误处理是自动的:如果回调抛出异常(或 future 出错),Span 状态会在 Span 结束前设为 SentrySpanStatusV2.error,随后重新抛出该错误。否则,状态默认为 ok。

你也可以通过 status setter 手动设置状态:

span.status = SentrySpanStatusV2.error;

没有与非类型化 SpanStatus 字符串对应的用法。旧 API 中显式设置的状态(例如 transaction.status = const SpanStatus.internalError())应迁移为 span.status = SentrySpanStatusV2.error——不过,抛出异常时会自动设为 error,其他情况则自动设为 ok,因此只有在需要覆盖默认值时才需手动赋值。

对于同步工作,请使用 Sentry.startSpanSync:

final config = Sentry.startSpanSync('parse-config', (_) {
  return Config.parse(raw);
});

这两种变体都可以自由嵌套——无论跨越同步还是异步边界,父子关系都会正确解析。如果某个 Span 未被采样,回调仍会运行并接收一个无操作 Span,因此所有 Span 操作都是安全的。

2.3 生命周期超出回调范围的 Span

当工作无法封装在单个回调中时,请使用 Sentry.startInactiveSpan——例如,Widget 生命周期、流订阅、平台通道往返调用。你必须手动调用 end(),其他 Span 不会自动成为它的子级。

final paymentSpan = Sentry.startInactiveSpan('payment',
    attributes: {'payment.provider': SentryAttribute.string('stripe')});

// ...later, from a different entry point
void onDeepLink(Uri uri) {
  paymentSpan.end();
}

父级关系。 parentSpan 默认为内部的 unset 哨兵(const UnsetSentrySpanV2()),表示“继承当前激活的 Span”。这与传入 parentSpan: null 明确不同,后者会强制创建没有父级的根 Span。传入显式的 SentrySpanV2,即可指定某个 Span 为父级。(startSpan / startSpanSync 也使用相同的默认值。)

追溯设置时间。 startSpan 和 startSpanSync 接受可选的 startTimestamp,而 span.end(endTimestamp: ...) 接受显式的结束时间。当工作的实际开始或结束时间早于你创建或结束 Span 的时间时,请使用这些参数——例如,通过平台通道测量的持续时间:

final paymentSpan = Sentry.startInactiveSpan('payment');
// ...native reports it started at `nativeStart` and ended at `nativeEnd`
paymentSpan.end(endTimestamp: nativeEnd);

// startTimestamp is available on the callback variants:
Sentry.startSpanSync('replay-import', (_) => importRows(),
    startTimestamp: measuredStart);
2.4 将 setData / setTag 迁移为类型化属性

流式 Span 使用类型化属性,而不是无类型的数据和标签:

// Before
transaction.setData('retry_count', 3);
transaction.setTag('payment.provider', 'stripe');

// After
span.setAttribute('retry_count', SentryAttribute.int(3));
span.setAttributes({
  'payment.provider': SentryAttribute.string('stripe'),
  'cache.hit': SentryAttribute.bool(true),
  'response_time_ms': SentryAttribute.double(12.5),
});
span.removeAttribute('old_key');
工厂方法Dart 类型
SentryAttribute.string(v)String
SentryAttribute.int(v)int
SentryAttribute.bool(v)bool
SentryAttribute.double(v)double
2.5 迁移 beforeSendTransaction

beforeSendTransaction 在流式模式下不起作用——不会创建事务,因此也不会调用该回调。请迁移其中的逻辑:

用例流式模式替代方案
按名称丢弃 Span使用 IgnoreSpanRule 配置 ignoreSpans
清除敏感数据 / PIIbeforeSendSpan
发送前修改 Span 数据beforeSendSpan

beforeSendSpan 会在每个 SentrySpanV2 发送前接收它。与其他 beforeSend 回调不同,它不能丢弃 Span——它只能修改内容(返回值为 FutureOr<void>)。请使用 ignoreSpans 丢弃 Span。

await Sentry.init((options) {
  options.traceLifecycle = SentryTraceLifecycle.stream;
  options.beforeSendSpan = (span) {
    span.removeAttribute('http.request.body');
  };
});

迁移逻辑后,移除 beforeSendTransaction 选项。

2.6 配置 ignoreSpans(可选)

ignoreSpans 会在采样前按名称丢弃 Span。匹配仅依据名称(Dart SDK 目前尚不支持按属性匹配)。

options.ignoreSpans = [
  IgnoreSpanRule.nameEquals('health-check'),
  IgnoreSpanRule.nameStartsWith('internal.'),
  IgnoreSpanRule.nameContains('metrics'),
  IgnoreSpanRule.nameEndsWith('.bg'),
];
工厂方法匹配规则
IgnoreSpanRule.nameEquals(String)Span 名称完全一致
IgnoreSpanRule.nameStartsWith(Pattern)名称前缀(String 或 RegExp)
IgnoreSpanRule.nameContains(Pattern)名称包含的子字符串(String 或 RegExp)
IgnoreSpanRule.nameEndsWith(String)名称后缀

被忽略的 Span 若有子级,这些子级会被重新关联到最近的记录中祖先,而不是被丢弃。

2.7 Flutter 自动埋点

无需更改代码:帧跟踪、应用启动、TTID/TTFD、导航、用户交互、HTTP、数据库和 GraphQL 埋点都会在 traceLifecycle 为 stream 时自动切换到流式 API。

2.8 采样

tracesSampleRate 和 tracesSampler 的工作方式不变。只有根 Span会被采样;子 Span 会继承根 Span 的采样决策。根 Span 未被采样时,回调仍会通过一个空操作 Span 执行。


第 3 阶段:验证

3.1 静态检查
dart analyze 2>&1 | head -30
# or for Flutter projects
flutter analyze 2>&1 | head -30

迁移后,预期不会出现新的分析器错误。

3.2 运行时验证

指示用户通过设置 options.debug = true 或检查网络流量进行验证:

  1. Envelope 内容类型:Span envelope 的发送内容类型为 application/vnd.sentry.items.span.v2+json,而不是事务 envelope。Span 会短暂缓冲后分批刷新,因此 envelope 出现前可能会有短暂延迟。
  2. Sentry 控制面板:每个 Span 完成后不久,就会在 Traces 视图中显示,无需等待整个事务完成。
  3. 空操作警告:如果出现日志行 startTransaction is not supported when traceLifecycle is 'stream',表示仍有旧的事务 API 调用——请找到并迁移这些调用。
3.3 常见问题
症状原因修复方法
日志:startTransaction is not supported when traceLifecycle is 'stream'流式模式下仍在调用旧的事务 API替换为 Sentry.startSpan / startSpanSync
Sentry.startSpan 未生成任何 SpantraceLifecycle 仍为 static(默认值),或已禁用追踪设置 options.traceLifecycle = SentryTraceLifecycle.stream,并设置 tracesSampleRate
从未调用 beforeSendTransaction流式模式下的预期行为将逻辑迁移至 beforeSendSpan 或 ignoreSpans,然后移除该选项
从 beforeSendSpan 返回值以丢弃 Span 没有作用beforeSendSpan 只能修改内容(返回值为 FutureOr<void>)使用 ignoreSpans 丢弃 Span
编译错误:startSpan 回调类型不匹配startSpan 的回调必须返回 Future<T>对同步工作使用 startSpanSync

快速参考

最简 Flutter 设置
import 'package:flutter/widgets.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

Future<void> main() async {
  await SentryFlutter.init((options) {
    options.dsn = '__DSN__';
    options.tracesSampleRate = 1.0;
    options.traceLifecycle = SentryTraceLifecycle.stream;
  }, appRunner: () => runApp(const MyApp()));

  await Sentry.startSpan('load-config', (span) async {
    span.setAttribute('source', SentryAttribute.string('remote'));
    await loadConfig();
  });
}
最简 Dart 设置
import 'package:sentry/sentry.dart';

Future<void> main() async {
  await Sentry.init((options) {
    options.dsn = '__DSN__';
    options.tracesSampleRate = 1.0;
    options.traceLifecycle = SentryTraceLifecycle.stream;
  });

  await Sentry.startSpan('main-task', (span) async {
    span.setAttribute('job.id', SentryAttribute.string('42'));
    await doWork();
  });
}
Span 控制参考
// Status (auto-set to error on throw, ok otherwise)
span.status = SentrySpanStatusV2.error; // error | ok

// Retroactive timing
Sentry.startSpanSync('task', (_) => work(), startTimestamp: start); // also on startSpan
span.end(endTimestamp: end);                                        // on any span

// Parenting (startSpan / startSpanSync / startInactiveSpan)
// default: inherit active span | parentSpan: someSpan -> explicit parent | parentSpan: null -> root
完整迁移检查清单
  • SDK 版本为 >=9.19.0(sentry / sentry_flutter)
  • 在每个初始化调用中设置 options.traceLifecycle = SentryTraceLifecycle.stream
  • 将所有 Sentry.startTransaction 调用替换为 Sentry.startSpan / startSpanSync / startInactiveSpan
  • 将所有 startChild 调用替换为嵌套的 Sentry.startSpan 调用
  • 将 Span 上的 setData / setTag 替换为类型化的 setAttribute / SentryAttribute
  • 将 beforeSendTransaction 逻辑迁移至 beforeSendSpan 或 ignoreSpans,并移除该选项
  • 为 startInactiveSpan Span 确保存在调用 end() 的路径
  • dart analyze / flutter analyze 通过
  • 在 Sentry Traces 视图中可以看到 Span

评论

支持 Markdown