Skip to content

Commit

Permalink
App start: Create transaction when no SentryNavigatorObserver is pr…
Browse files Browse the repository at this point in the history
…esent (#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
  • Loading branch information
buenaflor committed May 10, 2024
1 parent 4656f10 commit 40e30e1
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### Features

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

Expand Down
27 changes: 25 additions & 2 deletions flutter/lib/src/integrations/native_app_start_integration.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore_for_file: invalid_use_of_internal_member

import 'dart:async';

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

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

Expand Down Expand Up @@ -129,7 +130,6 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
description: entry.key as String,
));
} catch (e) {
// ignore: invalid_use_of_internal_member
_hub.options.logger(
SentryLevel.warning, 'Failed to parse native span times: $e');
continue;
Expand All @@ -149,6 +149,29 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
nativeSpanTimes: nativeSpanTimes);

setAppStartInfo(appStartInfo);

// When we don't have a SentryNavigatorObserver, a TTID transaction
// is not created therefore we need to create a transaction ourselves.
// We detect this by checking if the currentRouteName is null.
// This is a workaround since there is no api that tells us if
// the navigator observer exists and has been attached.
// The navigator observer also triggers much earlier so if it was attached
// it would have already set the routeName and the isCreated flag.
// The currentRouteName is always set during a didPush triggered
// by the navigator observer.
if (!SentryNavigatorObserver.isCreated &&
SentryNavigatorObserver.currentRouteName == null) {
const screenName = SentryNavigatorObserver.rootScreenName;
final transaction = hub.startTransaction(
screenName, SentrySpanOperations.uiLoad,
startTimestamp: appStartInfo.start);
final ttidSpan = transaction.startChild(
SentrySpanOperations.uiTimeToInitialDisplay,
description: '$screenName initial display',
startTimestamp: appStartInfo.start);
await ttidSpan.finish(endTimestamp: appStartInfo.end);
await transaction.finish(endTimestamp: appStartInfo.end);
}
});

options.addEventProcessor(NativeAppStartEventProcessor(_native, hub: hub));
Expand Down
11 changes: 10 additions & 1 deletion flutter/lib/src/navigation/sentry_navigator_observer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
_routeNameExtractor = routeNameExtractor,
_additionalInfoProvider = additionalInfoProvider,
_native = SentryFlutter.native {
_isCreated = true;
if (enableAutoTransactions) {
_hub.options.sdk.addIntegration('UINavigationTracing');
}
Expand Down Expand Up @@ -121,6 +122,11 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {

static String? _currentRouteName;

static bool _isCreated = false;

@internal
static bool get isCreated => _isCreated;

@internal
static String? get currentRouteName => _currentRouteName;

Expand Down Expand Up @@ -224,7 +230,7 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
}

if (name == '/') {
name = 'root /';
name = rootScreenName;
}
final transactionContext = SentryTransactionContext(
name,
Expand Down Expand Up @@ -366,6 +372,9 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
_completedDisplayTracking = Completer();
_timeToDisplayTracker?.clear();
}

@internal
static const String rootScreenName = 'root /';
}

/// This class makes it easier to record breadcrumbs for events of Flutters
Expand Down
13 changes: 13 additions & 0 deletions flutter/test/integrations/native_app_start_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,25 @@ import '../mocks.dart';
import '../mocks.mocks.dart';

void main() {
void setupMocks(Fixture fixture) {
when(fixture.hub.startTransaction('root /', 'ui.load',
description: null, startTimestamp: anyNamed('startTimestamp')))
.thenReturn(fixture.createTracer());
when(fixture.hub.configureScope(captureAny)).thenAnswer((_) {});
when(fixture.hub
.captureTransaction(any, traceContext: anyNamed('traceContext')))
.thenAnswer((_) async => SentryId.empty());
}

group('$NativeAppStartIntegration', () {
late Fixture fixture;

setUp(() {
TestWidgetsFlutterBinding.ensureInitialized();

fixture = Fixture();
setupMocks(fixture);

NativeAppStartIntegration.clearAppStartInfo();
});

Expand Down Expand Up @@ -257,6 +269,7 @@ void main() {
SentryFlutter.sentrySetupStartTime =
DateTime.fromMillisecondsSinceEpoch(15);

setupMocks(fixture);
fixture.getNativeAppStartIntegration().call(fixture.hub, fixture.options);

final processor = fixture.options.eventProcessors.first;
Expand Down

0 comments on commit 40e30e1

Please sign in to comment.