关于 sentry-span-streaming-dart
为 Dart 和 Flutter 项目提供 Sentry Span Streaming 迁移指导的技能,面向需要切换追踪生命周期的开发者。它涵盖版本与现有用法检查、API 和回调迁移,以及静态和运行时验证。
主要功能
- 01检测 Sentry SDK 版本与追踪用法
- 02配置流式 Span 生命周期
- 03将事务 API 迁移到 Span API
- 04迁移属性、回调与忽略规则
- 05提供静态和运行时验证步骤
适用场景
- 01在 Dart 项目启用 Span Streaming
- 02迁移 Flutter 项目的事务追踪
- 03处理旧 API 与回调的兼容调整
- 04验证迁移后的追踪数据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ getsentry/ sentry-for-ai --skill sentry-span-streaming-dart安装后在 Claude Code 中输入/sentry-span-streaming-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 |
| 清除敏感数据 / PII | beforeSendSpan |
| 发送前修改 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 或检查网络流量进行验证:
- Envelope 内容类型:Span envelope 的发送内容类型为
application/vnd.sentry.items.span.v2+json,而不是事务 envelope。Span 会短暂缓冲后分批刷新,因此 envelope 出现前可能会有短暂延迟。 - Sentry 控制面板:每个 Span 完成后不久,就会在 Traces 视图中显示,无需等待整个事务完成。
- 空操作警告:如果出现日志行
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 未生成任何 Span | traceLifecycle 仍为 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,并移除该选项 - 为
startInactiveSpanSpan 确保存在调用end()的路径 -
dart analyze/flutter analyze通过 - 在 Sentry Traces 视图中可以看到 Span