Skip to content

Commit 231f523

Browse files
authored
Bind root screen transaction to scope (#2756)
1 parent 4d90f92 commit 231f523

File tree

4 files changed

+56
-7
lines changed

4 files changed

+56
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
### Fixes
1111

1212
- Pass missing `captureFailedRequests` param to `FailedRequestInterceptor` ([#2744](https://github.com/getsentry/sentry-dart/pull/2744))
13+
- Bind root screen transaction to scope ([#2756](https://github.com/getsentry/sentry-dart/pull/2756))
1314

1415
### Dependencies
1516

flutter/lib/src/integrations/native_app_start_handler.dart

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,27 @@ class NativeAppStartHandler {
3737
// Create Transaction & Span
3838

3939
const screenName = 'root /';
40-
final transaction = _hub.startTransaction(
40+
final rootScreenTransaction = _hub.startTransaction(
4141
screenName,
4242
SentrySpanOperations.uiLoad,
4343
startTimestamp: appStartInfo.start,
4444
);
4545

46+
// Bind to scope if null
47+
await _hub.configureScope((scope) {
48+
scope.span ??= rootScreenTransaction;
49+
});
50+
4651
await options.timeToDisplayTracker.track(
47-
transaction,
52+
rootScreenTransaction,
4853
startTimestamp: appStartInfo.start,
4954
endTimestamp: appStartInfo.end,
5055
origin: SentryTraceOrigins.autoUiTimeToDisplay,
5156
);
5257

5358
SentryTracer sentryTracer;
54-
if (transaction is SentryTracer) {
55-
sentryTracer = transaction;
59+
if (rootScreenTransaction is SentryTracer) {
60+
sentryTracer = rootScreenTransaction;
5661
} else {
5762
return;
5863
}
@@ -62,8 +67,15 @@ class NativeAppStartHandler {
6267
sentryTracer.measurements[measurement.name] = appStartInfo.toMeasurement();
6368
await _attachAppStartSpans(appStartInfo, sentryTracer);
6469

70+
// Remove from scope
71+
await _hub.configureScope((scope) {
72+
if (scope.span == rootScreenTransaction) {
73+
scope.span = null;
74+
}
75+
});
76+
6577
// Finish Transaction
66-
await transaction.finish(endTimestamp: appStartInfo.end);
78+
await rootScreenTransaction.finish(endTimestamp: appStartInfo.end);
6779
}
6880

6981
_AppStartInfo? _infoNativeAppStart(

flutter/lib/src/navigation/sentry_navigator_observer.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
297297
_transaction = null;
298298
try {
299299
_hub.configureScope((scope) {
300-
if (scope.span == transaction) {
300+
if (transaction != null && scope.span == transaction) {
301301
scope.span = null;
302302
}
303303
});

flutter/test/integrations/native_app_start_handler_test.dart

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ void main() {
2323
startTimestamp: anyNamed('startTimestamp'),
2424
)).thenReturn(fixture.tracer);
2525

26-
when(fixture.hub.configureScope(captureAny)).thenAnswer((_) {});
26+
when(fixture.hub.configureScope(captureAny)).thenAnswer((invocation) {
27+
final callback = invocation.positionalArguments[0] as ScopeCallback;
28+
callback(fixture.scope);
29+
return null;
30+
});
31+
2732
when(fixture.hub.captureTransaction(
2833
any,
2934
traceContext: anyNamed('traceContext'),
@@ -125,6 +130,25 @@ void main() {
125130
traceContext: captureAnyNamed('traceContext'),
126131
));
127132
});
133+
134+
test('added transaction is bound to scope', () async {
135+
await fixture.call(
136+
appStartEnd: DateTime.fromMillisecondsSinceEpoch(10),
137+
);
138+
expect(fixture.scope.setSpans.length, 2);
139+
expect(fixture.scope.setSpans[0], fixture.tracer);
140+
expect(fixture.scope.setSpans[1], isNull);
141+
});
142+
143+
test('added transaction is not bound to scope if already set', () async {
144+
final alreadySet = MockSentryTracer();
145+
fixture.scope.span = alreadySet;
146+
await fixture.call(
147+
appStartEnd: DateTime.fromMillisecondsSinceEpoch(10),
148+
);
149+
expect(fixture.scope.setSpans.length, 1);
150+
expect(fixture.scope.setSpans[0], alreadySet);
151+
});
128152
});
129153

130154
group('App start spans', () {
@@ -306,6 +330,7 @@ class Fixture {
306330
final options = SentryFlutterOptions(dsn: fakeDsn);
307331
final nativeBinding = MockSentryNativeBinding();
308332
final hub = MockHub();
333+
final scope = MockScope();
309334

310335
late final tracer = SentryTracer(
311336
SentryTransactionContext(
@@ -343,3 +368,14 @@ class Fixture {
343368
return args[0] as SentryTransaction;
344369
}
345370
}
371+
372+
class MockScope extends Mock implements Scope {
373+
final setSpans = <ISentrySpan?>[];
374+
375+
@override
376+
ISentrySpan? get span => setSpans.lastOrNull;
377+
@override
378+
set span(ISentrySpan? value) {
379+
setSpans.add(value);
380+
}
381+
}

0 commit comments

Comments
 (0)