From f2668bb2348fba9069e6272d48e0f36cf9282e6c Mon Sep 17 00:00:00 2001 From: GIancarlo Buenaflor Date: Tue, 6 Feb 2024 13:10:26 +0100 Subject: [PATCH] poc --- flutter/example/lib/auto_close_screen.dart | 4 ++- flutter/example/lib/main.dart | 1 + .../navigation/sentry_navigator_observer.dart | 30 +++++++++++-------- flutter/lib/src/sentry_flutter.dart | 13 +++++++- flutter/lib/src/sentry_flutter_options.dart | 4 +++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/flutter/example/lib/auto_close_screen.dart b/flutter/example/lib/auto_close_screen.dart index 3a1866e564..9fa368700a 100644 --- a/flutter/example/lib/auto_close_screen.dart +++ b/flutter/example/lib/auto_close_screen.dart @@ -26,7 +26,9 @@ class AutoCloseScreenState extends State { final childSpan = activeSpan?.startChild('complex operation', description: 'running a $delayInSeconds seconds operation'); await Future.delayed(const Duration(seconds: delayInSeconds)); - childSpan?.finish(); + await childSpan?.finish(); + await Future.delayed(const Duration(seconds: 2)); + SentryFlutter.reportFullDisplay(); // ignore: use_build_context_synchronously Navigator.of(context).pop(); } diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 2e4b2e7518..250a900b32 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -71,6 +71,7 @@ Future setupSentry(AppRunner appRunner, String dsn, options.attachScreenshot = true; options.screenshotQuality = SentryScreenshotQuality.low; options.attachViewHierarchy = true; + options.enableTimeToFullDisplayTracing = true; // We can enable Sentry debug logging during development. This is likely // going to log too much for your app, but can be useful when figuring out // configuration issues, e.g. finding out why your events are not uploaded. diff --git a/flutter/lib/src/navigation/sentry_navigator_observer.dart b/flutter/lib/src/navigation/sentry_navigator_observer.dart index 37c5a75221..6e2f7dd99e 100644 --- a/flutter/lib/src/navigation/sentry_navigator_observer.dart +++ b/flutter/lib/src/navigation/sentry_navigator_observer.dart @@ -99,6 +99,9 @@ class SentryNavigatorObserver extends RouteObserver> { static String? get currentRouteName => _currentRouteName; static var startTime = DateTime.now(); static ISentrySpan? ttidSpan; + static ISentrySpan? ttfdSpan; + static var ttfdStartTime = DateTime.now(); + static Stopwatch? ttfdStopwatch; @override void didPush(Route route, Route? previousRoute) { @@ -114,14 +117,12 @@ class SentryNavigatorObserver extends RouteObserver> { ); _finishTransaction(); - - var routeName = route.settings.name ?? 'Unknown'; - _startTransaction(route); // Start timing DateTime? approximationEndTimestamp; int? approximationDurationMillis; + final routeName = _getRouteName(route); SchedulerBinding.instance.addPostFrameCallback((timeStamp) { approximationEndTimestamp = DateTime.now(); @@ -129,11 +130,14 @@ class SentryNavigatorObserver extends RouteObserver> { approximationEndTimestamp!.millisecond - startTime.millisecond; }); - SentryDisplayTracker().startTimeout(routeName, () { + SentryDisplayTracker().startTimeout(routeName ?? 'Unknown', () { + if (routeName == '/') { + // TODO: Does TTID have to be completely in line with app start? + // If yes, how do we access the appstart metrics + } _transaction2?.setMeasurement( 'time_to_initial_display', approximationDurationMillis!, unit: DurationSentryMeasurementUnit.milliSecond); - ttidSpan?.setTag('measurement', 'approximation'); ttidSpan?.finish(endTimestamp: approximationEndTimestamp!); }); } @@ -244,8 +248,6 @@ class SentryNavigatorObserver extends RouteObserver> { origin: SentryTraceOrigins.autoNavigationRouteObserver, ); - // IMPORTANT -> we need to wait for ttid/ttfd children to finish AND wait [autoFinishAfter] afterwards so the user can add additional spans - // right now it auto finishes when ttid/ttfd finishes but that doesn't allow the user to add spans within the idle timeout _transaction2 = _hub.startTransactionWithContext( transactionContext2, waitForChildren: true, @@ -275,13 +277,17 @@ class SentryNavigatorObserver extends RouteObserver> { } startTime = DateTime.now(); - ttidSpan = _transaction2?.startChild('ui.load.initial_display'); + ttidSpan = _transaction2?.startChild('ui.load.initial_display', description: '$name initial display'); ttidSpan?.origin = 'auto.ui.time_to_display'; - ttidSpan?.setData('test', 'cachea'); - // Needs to finish after 30 seconds - // If not then it will finish with status deadline exceeded - // final ttfdSpan = _transaction2?.startChild('ui.load.full_display'); + // TODO: Needs to finish max within 30 seconds + // If timeout exceeds then it will finish with status deadline exceeded + // What to do if root also has TTFD but it's not finished yet and we start navigating to another? + // How to track the time that 30 sec have passed? + if ((_hub.options as SentryFlutterOptions).enableTimeToFullDisplayTracing && name != 'root ("/")') { + ttfdStopwatch = Stopwatch()..start(); + ttfdSpan = _transaction2?.startChild('ui.load.full_display', description: '$name full display'); + } if (arguments != null) { _transaction2?.setData('route_settings_arguments', arguments); diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 3ef591183d..efe3305efa 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -243,7 +243,18 @@ mixin SentryFlutter { } } - static void reportFullDisplay() {} + /// Reports the time it took for the screen to be fully displayed. + static void reportFullDisplay() { + if (SentryNavigatorObserver.ttfdStopwatch?.elapsedMilliseconds != null) { + SentryNavigatorObserver.ttfdStopwatch?.stop(); + SentryNavigatorObserver.ttfdSpan?.setMeasurement( + 'time_to_full_display', + SentryNavigatorObserver.ttfdStopwatch!.elapsedMilliseconds, + unit: DurationSentryMeasurementUnit.milliSecond); + SentryNavigatorObserver.ttfdStopwatch?.reset(); + } + SentryNavigatorObserver.ttfdSpan?.finish(); + } @internal static SentryNative? get native => _native; diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index ee722f8e9e..727fc7f6c0 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -223,6 +223,10 @@ class SentryFlutterOptions extends SentryOptions { /// Read timeout. This will only be synced to the Android native SDK. Duration readTimeout = Duration(seconds: 5); + /// Enable or disable the tracing of time to full display. + /// This feature requires using the [Routing Instrumentation](https://docs.sentry.io/platforms/flutter/integrations/routing-instrumentation/). + bool enableTimeToFullDisplayTracing = false; + /// By using this, you are disabling native [Breadcrumb] tracking and instead /// you are just tracking [Breadcrumb]s which result from events available /// in the current Flutter environment.