sentry-span-streaming-dart について
Dart と Flutter の Sentry トレースを Span Streaming に移行するためのスキルです。SDK と既存の使用状況の確認から、API やコールバックの変更、静的・実行時の検証まで案内します。
主な機能
- 01Sentry SDK のバージョンと追跡用途を検出
- 02ストリーミング Span のライフサイクルを設定
- 03トランザクション API を Span API に移行
- 04属性、コールバック、除外ルールを変更
- 05静的・実行時の検証手順を提示
活用シーン
- 01Dart プロジェクトで Span Streaming を有効化
- 02Flutter のトランザクション追跡を移行
- 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 Streaming への移行(Dart/Flutter)
デフォルトのトランザクションベースのトレースライフサイクル(SentryTraceLifecycle.static)から、スパンストリーミング(SentryTraceLifecycle.stream)に移行します。スパンストリーミングでは、スパンは最後にトランザクションにまとめて送信されるのではなく、完了するたびに個別に送信されます。
このスキルでは、Dart および Flutter SDK(sentry、sentry_flutter、sentry_dio、sentry_sqflite、sentry_drift などのインテグレーションパッケージ)を扱います。JavaScript については、Span Streaming(JavaScript)を参照してください。Python については、Span Streaming(Python)を参照してください。
このスキルを使う場面
- ユーザーが Dart または Flutter プロジェクトで「スパンストリーミングを有効にする」または「スパンストリーミングに移行する」と依頼した場合
- ユーザーがトランザクションベースの配信から、スパンをストリーミング配信する方式に切り替えたい場合
- ユーザーが
traceLifecycle、SentryTraceLifecycle.stream、Sentry.startSpan、またはSentrySpanV2に言及した場合 - ユーザーがスパン配信の低レイテンシ化、またはスパンごとの処理を求めている場合
フェーズ 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
スパンストリーミングには、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 の呼び出しに置き換えます。ゾーンによって親子関係は自動的に設定されます |
スパンに対する setData / setTag | 型付きの span.setAttribute(...) / SentryAttribute に置き換えます |
beforeSendTransaction コールバック | ストリーミングモードでは呼び出されません。ロジックを beforeSendSpan または ignoreSpans に移行します |
既存の beforeSendSpan / ignoreSpans の使用 | ストリーミングの仕様に照らして確認します。beforeSendSpan は変更のみを行い、ignoreSpans は名前のみを照合します(2.5/2.6 を参照) |
| 自動計装のみ(手動のスパンなし) | オプションを有効にするだけで、インテグレーションが自動的に切り替わります |
フェーズ 2:移行
前提条件: トレーシングが有効(tracesSampleRate または tracesSampler が設定済み)な sentry / sentry_flutter >=9.19.0。
2.1 スパンストリーミングを有効にする
初期化オプションで 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;
});
トレーシングシステムは一度に 1 つだけ使用できます。
streamモードでは、トランザクション API(Sentry.startTransaction、ISentrySpan.startChild)は何も行わず、警告をログに記録します。staticモード(デフォルト)では、新しいスパン API(Sentry.startSpan)は何も行いません。- 自動計装は、この設定に応じて適切な API に自動的に切り替わります。
2.2 トランザクションをスパンに置き換える
Sentry.startSpan は非同期コールバックを実行し、返された future が完了するとスパンを終了します。ネストした呼び出しの親子関係はゾーンを通じて自動的に設定されます。移行先となる 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 がエラーになった場合)、スパンの終了前にステータスが SentrySpanStatusV2.error に設定され、エラーが再スローされます。それ以外の場合、ステータスはデフォルトで ok になります。
status セッターを使ってステータスを手動で設定することもできます。
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);
});
どちらのバリアントも自由にネストできます。同期/非同期の境界をまたいでも、親子関係は正しく解決されます。スパンがサンプリングされない場合でもコールバックは実行され、no-op のスパンを受け取るため、すべてのスパン操作を安全に行えます。
2.3 コールバックの終了後も存続するスパン
ウィジェットのライフサイクル、ストリームのサブスクリプション、プラットフォームチャネルの往復処理など、単一のコールバックで処理を囲めない場合は、Sentry.startInactiveSpan を使用します。end() を手動で呼び出す必要があり、ほかのスパンが自動的にその子になることはありません。
final paymentSpan = Sentry.startInactiveSpan('payment',
attributes: {'payment.provider': SentryAttribute.string('stripe')});
// ...later, from a different entry point
void onDeepLink(Uri uri) {
paymentSpan.end();
}
親子関係。 parentSpan のデフォルト値は内部の 未設定 センチネル(const UnsetSentrySpanV2())です。これは「現在アクティブなスパンを継承する」ことを意味します。parentSpan: null を渡す場合とは明確に異なり、こちらは親のないルートスパンを強制します。特定のスパンを親にするには、明示的な SentrySpanV2 を渡します。(同じデフォルト値は startSpan / startSpanSync にも適用されます。)
遡及的なタイミング設定。 startSpan と startSpanSync は省略可能な startTimestamp を受け取り、span.end(endTimestamp: ...) は明示的な終了時刻を受け取ります。処理の実際の開始時刻や終了時刻より後にスパンを作成または終了する場合に使用します。たとえば、プラットフォームチャネルで計測した処理時間に使用できます。
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 を型付き属性に移行する
ストリーミングスパンでは、型のないデータやタグの代わりに型付き属性を使用します。
// 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 は効果がありません。トランザクションが作成されないため、このコールバックは呼び出されません。そのロジックを移行してください。
| 用途 | ストリーミングでの置き換え先 |
|---|---|
| 名前でスパンを破棄する | IgnoreSpanRule を指定した ignoreSpans |
| 機密データ / PII をマスキングする | beforeSendSpan |
| 送信前にスパンデータを変更する | beforeSendSpan |
beforeSendSpan は、各 SentrySpanV2 が送信される前に受け取ります。他の beforeSend コールバックとは異なり、スパンを破棄できません。変更のみを行い、戻り値は FutureOr<void> です。破棄には ignoreSpans を使用します。
await Sentry.init((options) {
options.traceLifecycle = SentryTraceLifecycle.stream;
options.beforeSendSpan = (span) {
span.removeAttribute('http.request.body');
};
});
ロジックを移行した後、beforeSendTransaction オプションを削除してください。
2.6 ignoreSpans を設定する(任意)
ignoreSpans は、サンプリング前に名前でスパンを破棄します。一致判定は名前のみを対象とします(Dart SDK では属性による一致判定はまだサポートされていません)。
options.ignoreSpans = [
IgnoreSpanRule.nameEquals('health-check'),
IgnoreSpanRule.nameStartsWith('internal.'),
IgnoreSpanRule.nameContains('metrics'),
IgnoreSpanRule.nameEndsWith('.bg'),
];
| ファクトリー | 一致対象 |
|---|---|
IgnoreSpanRule.nameEquals(String) | スパン名の完全一致 |
IgnoreSpanRule.nameStartsWith(Pattern) | 名前の接頭辞(String または RegExp) |
IgnoreSpanRule.nameContains(Pattern) | 名前に含まれる部分文字列(String または RegExp) |
IgnoreSpanRule.nameEndsWith(String) | 名前の接尾辞 |
無視されたスパンに子がある場合、子は破棄されず、最も近い記録中の祖先に付け替えられます。
2.7 Flutter の自動計装
コードの変更は不要です。フレーム追跡、アプリ起動、TTID / TTFD、ナビゲーション、ユーザー操作、HTTP、データベース、GraphQL の各計装は、traceLifecycle が stream の場合、自動的にストリーミング API に切り替わります。
2.8 サンプリング
tracesSampleRate と tracesSampler はこれまでどおり動作します。サンプリングされるのはルートスパンのみで、子スパンはルートの判定を継承します。ルートスパンがサンプリングされない場合も、コールバックは no-op スパンを使って実行されます。
フェーズ 3:検証
3.1 静的チェック
dart analyze 2>&1 | head -30
# or for Flutter projects
flutter analyze 2>&1 | head -30
移行による新たなアナライザーエラーがないことを確認してください。
3.2 実行時の検証
options.debug = true を設定するか、ネットワークを調査して確認するようユーザーに指示してください。
- エンベロープのコンテンツタイプ:スパンのエンベロープは、トランザクションのエンベロープではなく、
application/vnd.sentry.items.span.v2+jsonのコンテンツタイプで送信されます。スパンは短時間バッファリングされてからバッチでフラッシュされるため、エンベロープが表示されるまで少し時間がかかります。 - Sentry ダッシュボード:各スパンの完了後すぐに、トランザクション全体の終了を待たずに、トレースビューにスパンが表示されます。
- no-op の警告:ログ行
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 でスパンが生成されない | traceLifecycle がまだ static(デフォルト)のままになっている、またはトレーシングが無効になっています | options.traceLifecycle = SentryTraceLifecycle.stream と tracesSampleRate を設定します |
beforeSendTransaction が一度も呼び出されない | ストリーミングモードでは想定される動作です | ロジックを beforeSendSpan または ignoreSpans に移行してから、削除します |
beforeSendSpan から値を返してもスパンが破棄されない | beforeSendSpan は変更のみを行います(戻り値は FutureOr<void>) | スパンを破棄するには ignoreSpans を使用します |
コンパイルエラー: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();
});
}
スパン制御のリファレンス
// 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呼び出しに置き換えている - スパンの
setData/setTagを、型指定されたsetAttribute/SentryAttributeに置き換えている -
beforeSendTransactionのロジックをbeforeSendSpanまたはignoreSpansに移行し、オプションを削除している -
startInactiveSpanのスパンに、確実にend()が呼び出される経路がある -
dart analyze/flutter analyzeが成功する - Sentry の「Traces」ビューにスパンが表示される