Skip to content

Commit 40e30e1

Browse files
authored
App start: Create transaction when no SentryNavigatorObserver is present (#2017)
* commit * Update * Remove print * Remove comments * Update * Add linting * Update CHANGELOG * Update CHANGELOG.md * Update naming * Update naming * Update naming * Create transaction * Update description from first frame render to initial frame render * Update * update * dart format * Update comments * Update * Update * Update * Update * Update * Fix tests * Fix test * Add unused import * Update * Update * Updat * Fix tests * Fix tests * Fix analyze * Update * Update * Updaet * Update * Add additional flag and update comment
1 parent 4656f10 commit 40e30e1

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
### Features
1010

11+
- Create app start transaction when no `SentryNavigatorObserver` is present ([#2017](https://github.com/getsentry/sentry-dart/pull/2017))
1112
- Adds native spans to app start transaction ([#2027](https://github.com/getsentry/sentry-dart/pull/2027))
1213
- Adds app start spans to first transaction ([#2009](https://github.com/getsentry/sentry-dart/pull/2009))
1314

flutter/lib/src/integrations/native_app_start_integration.dart

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// ignore_for_file: invalid_use_of_internal_member
2+
13
import 'dart:async';
24

35
import 'package:meta/meta.dart';
@@ -96,7 +98,6 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
9698

9799
if (options.autoAppStart) {
98100
// We only assign the current time if it's not already set - this is useful in tests
99-
// ignore: invalid_use_of_internal_member
100101
_native.appStartEnd ??= options.clock();
101102
appStartEndDateTime = _native.appStartEnd;
102103

@@ -129,7 +130,6 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
129130
description: entry.key as String,
130131
));
131132
} catch (e) {
132-
// ignore: invalid_use_of_internal_member
133133
_hub.options.logger(
134134
SentryLevel.warning, 'Failed to parse native span times: $e');
135135
continue;
@@ -149,6 +149,29 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
149149
nativeSpanTimes: nativeSpanTimes);
150150

151151
setAppStartInfo(appStartInfo);
152+
153+
// When we don't have a SentryNavigatorObserver, a TTID transaction
154+
// is not created therefore we need to create a transaction ourselves.
155+
// We detect this by checking if the currentRouteName is null.
156+
// This is a workaround since there is no api that tells us if
157+
// the navigator observer exists and has been attached.
158+
// The navigator observer also triggers much earlier so if it was attached
159+
// it would have already set the routeName and the isCreated flag.
160+
// The currentRouteName is always set during a didPush triggered
161+
// by the navigator observer.
162+
if (!SentryNavigatorObserver.isCreated &&
163+
SentryNavigatorObserver.currentRouteName == null) {
164+
const screenName = SentryNavigatorObserver.rootScreenName;
165+
final transaction = hub.startTransaction(
166+
screenName, SentrySpanOperations.uiLoad,
167+
startTimestamp: appStartInfo.start);
168+
final ttidSpan = transaction.startChild(
169+
SentrySpanOperations.uiTimeToInitialDisplay,
170+
description: '$screenName initial display',
171+
startTimestamp: appStartInfo.start);
172+
await ttidSpan.finish(endTimestamp: appStartInfo.end);
173+
await transaction.finish(endTimestamp: appStartInfo.end);
174+
}
152175
});
153176

154177
options.addEventProcessor(NativeAppStartEventProcessor(_native, hub: hub));

flutter/lib/src/navigation/sentry_navigator_observer.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
8686
_routeNameExtractor = routeNameExtractor,
8787
_additionalInfoProvider = additionalInfoProvider,
8888
_native = SentryFlutter.native {
89+
_isCreated = true;
8990
if (enableAutoTransactions) {
9091
_hub.options.sdk.addIntegration('UINavigationTracing');
9192
}
@@ -121,6 +122,11 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
121122

122123
static String? _currentRouteName;
123124

125+
static bool _isCreated = false;
126+
127+
@internal
128+
static bool get isCreated => _isCreated;
129+
124130
@internal
125131
static String? get currentRouteName => _currentRouteName;
126132

@@ -224,7 +230,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
224230
}
225231

226232
if (name == '/') {
227-
name = 'root /';
233+
name = rootScreenName;
228234
}
229235
final transactionContext = SentryTransactionContext(
230236
name,
@@ -366,6 +372,9 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
366372
_completedDisplayTracking = Completer();
367373
_timeToDisplayTracker?.clear();
368374
}
375+
376+
@internal
377+
static const String rootScreenName = 'root /';
369378
}
370379

371380
/// This class makes it easier to record breadcrumbs for events of Flutters

flutter/test/integrations/native_app_start_integration_test.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,25 @@ import '../mocks.dart';
1212
import '../mocks.mocks.dart';
1313

1414
void main() {
15+
void setupMocks(Fixture fixture) {
16+
when(fixture.hub.startTransaction('root /', 'ui.load',
17+
description: null, startTimestamp: anyNamed('startTimestamp')))
18+
.thenReturn(fixture.createTracer());
19+
when(fixture.hub.configureScope(captureAny)).thenAnswer((_) {});
20+
when(fixture.hub
21+
.captureTransaction(any, traceContext: anyNamed('traceContext')))
22+
.thenAnswer((_) async => SentryId.empty());
23+
}
24+
1525
group('$NativeAppStartIntegration', () {
1626
late Fixture fixture;
1727

1828
setUp(() {
1929
TestWidgetsFlutterBinding.ensureInitialized();
2030

2131
fixture = Fixture();
32+
setupMocks(fixture);
33+
2234
NativeAppStartIntegration.clearAppStartInfo();
2335
});
2436

@@ -257,6 +269,7 @@ void main() {
257269
SentryFlutter.sentrySetupStartTime =
258270
DateTime.fromMillisecondsSinceEpoch(15);
259271

272+
setupMocks(fixture);
260273
fixture.getNativeAppStartIntegration().call(fixture.hub, fixture.options);
261274

262275
final processor = fixture.options.eventProcessors.first;

0 commit comments

Comments
 (0)