diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aabdc80c..5b5cf74b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ Navigator.push( - Start missing TTFD for root screen transaction ([#2332](https://github.com/getsentry/sentry-dart/pull/2332)) - Accessing invalid json fields from `fetchNativeAppStart` should return null ([#2340](https://github.com/getsentry/sentry-dart/pull/2340)) - Error when calling `SentryFlutter.reportFullyDisplayed()` twice ([#2339](https://github.com/getsentry/sentry-dart/pull/2339)) +- TTFD measurements should only be added for successful TTFD spans ([#2348](https://github.com/getsentry/sentry-dart/pull/2348)) ### Deprecate diff --git a/flutter/lib/src/navigation/time_to_full_display_tracker.dart b/flutter/lib/src/navigation/time_to_full_display_tracker.dart index 6c9130eeb..52cd27f4b 100644 --- a/flutter/lib/src/navigation/time_to_full_display_tracker.dart +++ b/flutter/lib/src/navigation/time_to_full_display_tracker.dart @@ -71,7 +71,7 @@ class TimeToFullDisplayTracker { final endTimestamp = timestamp ?? _endTimestampProvider(); if (ttfdSpan == null || - ttfdSpan.finished == true || + ttfdSpan.finished || startTimestamp == null || endTimestamp == null) { options.logger( @@ -83,14 +83,29 @@ class TimeToFullDisplayTracker { return; } - _setTTFDMeasurement(startTimestamp, endTimestamp); - await ttfdSpan.finish( - status: - timestamp != null ? SpanStatus.ok() : SpanStatus.deadlineExceeded(), - endTimestamp: endTimestamp, - ); - _completedTTFDTracking.complete(); - clear(); + // If a timestamp is provided, the operation was successful; otherwise, it timed out + final status = + timestamp != null ? SpanStatus.ok() : SpanStatus.deadlineExceeded(); + try { + // Should only add measurements if the span is successful + if (status == SpanStatus.ok()) { + _setTTFDMeasurement(startTimestamp, endTimestamp); + } + await ttfdSpan.finish( + status: status, + endTimestamp: endTimestamp, + ); + } catch (e, stackTrace) { + options.logger( + SentryLevel.error, + 'Failed to finish TTFD span', + exception: e, + stackTrace: stackTrace, + ); + } finally { + _completedTTFDTracking.complete(); + clear(); + } } void _setTTFDMeasurement(DateTime startTimestamp, DateTime endTimestamp) { diff --git a/flutter/test/navigation/time_to_full_display_tracker_test.dart b/flutter/test/navigation/time_to_full_display_tracker_test.dart index a2adf62ae..2dc62ecd7 100644 --- a/flutter/test/navigation/time_to_full_display_tracker_test.dart +++ b/flutter/test/navigation/time_to_full_display_tracker_test.dart @@ -44,6 +44,7 @@ void main() { final differenceInSeconds = actualEndTimestamp.difference(expectedEndTimestamp).inSeconds.abs(); expect(differenceInSeconds, lessThanOrEqualTo(1)); + expect(transaction.measurements, isNotEmpty); }); test( @@ -66,6 +67,7 @@ void main() { expect(ttfdSpan.status, equals(SpanStatus.deadlineExceeded())); expect(ttfdSpan.context.description, equals('Current route full display')); expect(ttfdSpan.origin, equals(SentryTraceOrigins.manualUiTimeToDisplay)); + expect(transaction.measurements, isEmpty); }); test('finishing ttfd twice does not throw', () async {