Skip to content

Commit ab8bddc

Browse files
committed
working ttid and ttfd for non root app start navigations
1 parent 69e4d3e commit ab8bddc

File tree

3 files changed

+67
-74
lines changed

3 files changed

+67
-74
lines changed

flutter/example/lib/main.dart

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Future<void> setupSentry(AppRunner appRunner, String dsn,
7777
// going to log too much for your app, but can be useful when figuring out
7878
// configuration issues, e.g. finding out why your events are not uploaded.
7979
options.debug = true;
80+
options.enableTimeToFullDisplayTracing = true;
8081

8182
options.maxRequestBodySize = MaxRequestBodySize.always;
8283
options.maxResponseBodySize = MaxResponseBodySize.always;

flutter/lib/src/navigation/navigation_timing_manager.dart

+66-73
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,6 @@ class NavigationTimingManager {
4545
}
4646

4747
void startMeasurement(String routeName) async {
48-
final options = _hub.options is SentryFlutterOptions
49-
// ignore: invalid_use_of_internal_member
50-
? _hub.options as SentryFlutterOptions
51-
: null;
52-
53-
// This marks the start timestamp of both TTID and TTFD spans
5448
_startTimestamp = DateTime.now();
5549

5650
// This has multiple branches
@@ -62,107 +56,106 @@ class NavigationTimingManager {
6256
// App start - this is a special edge case that only happens once
6357
AppStartTracker().onAppStartComplete((appStartInfo) {
6458
// Create a transaction based on app start start time
65-
// Create ttidSpan and finish immediately with the app start start & end time
66-
// This is a small workaround to pass the correct time stamps since we mutate
59+
// Then create ttidSpan and finish immediately with the app start start & end time
60+
// This is a small workaround to pass the correct time stamps since we cannot mutate
6761
// timestamps of transactions or spans in history
6862
if (appStartInfo != null) {
6963
final transaction = _transactionManager?.startTransaction(
7064
routeName, appStartInfo.start);
7165
if (transaction != null) {
72-
final ttidSpan = _startTimeToInitialDisplaySpan(
73-
routeName, transaction, appStartInfo.start);
66+
final ttidSpan = _createTTIDSpan(transaction, routeName, appStartInfo.start);
7467
ttidSpan.finish(endTimestamp: appStartInfo.end);
7568
}
7669
}
7770
});
7871
} else {
79-
DateTime? approximationEndTime;
80-
final endTimeCompleter = Completer<DateTime>();
8172
final transaction =
8273
_transactionManager?.startTransaction(routeName, _startTimestamp!);
8374

84-
if (transaction != null) {
85-
if (options?.enableTimeToFullDisplayTracing == true) {
86-
_ttfdSpan = transaction.startChild('ui.load.full_display',
87-
description: '$routeName full display',
88-
startTimestamp: _startTimestamp!);
89-
90-
_ttfdSpan = _startTimeToFullDisplaySpan(
91-
routeName, transaction, _startTimestamp!);
92-
}
93-
_ttidSpan = _startTimeToInitialDisplaySpan(
94-
routeName, transaction, _startTimestamp!);
75+
if (transaction == null) {
76+
return;
9577
}
9678

97-
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
98-
approximationEndTime = DateTime.now();
99-
endTimeCompleter.complete(approximationEndTime);
100-
});
79+
_initializeSpans(transaction, routeName, _startTimestamp!);
10180

102-
final strategyDecision =
103-
await SentryDisplayTracker().decideStrategyWithTimeout2(routeName);
104-
105-
switch (strategyDecision) {
106-
case StrategyDecision.manual:
107-
final endTimestamp = DateTime.now();
108-
final duration = endTimestamp.millisecondsSinceEpoch -
109-
_startTimestamp!.millisecondsSinceEpoch;
110-
_endTimeToInitialDisplaySpan(_ttidSpan!, transaction!, endTimestamp, duration);
111-
break;
112-
case StrategyDecision.approximation:
113-
if (approximationEndTime == null) {
114-
await endTimeCompleter.future;
115-
}
116-
final duration = approximationEndTime!.millisecondsSinceEpoch -
117-
_startTimestamp!.millisecondsSinceEpoch;
118-
_endTimeToInitialDisplaySpan(
119-
_ttidSpan!, transaction!, approximationEndTime!, duration);
120-
await _ttidSpan?.finish(endTimestamp: approximationEndTime);
121-
break;
122-
default:
123-
print('Unknown strategy decision: $strategyDecision');
124-
}
81+
final endTimestamp = await _determineEndTime(routeName);
82+
83+
final duration = endTimestamp.difference(_startTimestamp!).inMilliseconds;
84+
_finishSpan(_ttidSpan!, transaction, 'time_to_initial_display', duration,
85+
endTimestamp);
12586
}
12687
}
12788

89+
Future<DateTime> _determineEndTime(String routeName) async {
90+
DateTime? approximationEndTime;
91+
final endTimeCompleter = Completer<DateTime>();
92+
93+
SchedulerBinding.instance.addPostFrameCallback((_) {
94+
approximationEndTime = DateTime.now();
95+
endTimeCompleter.complete(approximationEndTime!);
96+
});
97+
98+
final strategyDecision =
99+
await SentryDisplayTracker().decideStrategyWithTimeout2(routeName);
100+
101+
if (strategyDecision == StrategyDecision.manual &&
102+
!endTimeCompleter.isCompleted) {
103+
approximationEndTime = DateTime.now();
104+
endTimeCompleter.complete(approximationEndTime);
105+
} else if (!endTimeCompleter.isCompleted) {
106+
// If the decision is not manual and the completer hasn't been completed, await it.
107+
await endTimeCompleter.future;
108+
}
109+
110+
return approximationEndTime!;
111+
}
112+
128113
void reportInitiallyDisplayed(String routeName) {
129114
SentryDisplayTracker().reportManual2(routeName);
130115
}
131116

132117
void reportFullyDisplayed() {
133-
final endTime = DateTime.now();
118+
final endTimestamp = DateTime.now();
134119
final transaction = Sentry.getSpan();
135-
final duration = endTime.millisecondsSinceEpoch -
136-
_startTimestamp!.millisecondsSinceEpoch;
137-
if (_ttfdSpan != null && transaction != null) {
138-
_endTimeToFullDisplaySpan(_ttfdSpan!, transaction, endTime, duration);
120+
final duration = endTimestamp.difference(_startTimestamp!).inMilliseconds;
121+
if (_ttidSpan == null || transaction == null) {
122+
return;
139123
}
124+
_finishSpan(_ttfdSpan!, transaction, 'time_to_full_display', duration, endTimestamp);
140125
}
141126

142-
static ISentrySpan _startTimeToInitialDisplaySpan(
143-
String routeName, ISentrySpan transaction, DateTime startTimestamp) {
144-
return transaction.startChild(SentryTraceOrigins.uiTimeToInitialDisplay,
145-
description: '$routeName initial display',
146-
startTimestamp: startTimestamp);
127+
void _initializeSpans(ISentrySpan? transaction, String routeName, DateTime startTimestamp) {
128+
final options = _hub.options is SentryFlutterOptions
129+
// ignore: invalid_use_of_internal_member
130+
? _hub.options as SentryFlutterOptions
131+
: null;
132+
if (transaction == null) return;
133+
_ttidSpan = _createTTIDSpan(transaction, routeName, startTimestamp);
134+
if (options?.enableTimeToFullDisplayTracing == true) {
135+
_ttfdSpan = _createTTFDSpan(transaction, routeName, startTimestamp);
136+
}
147137
}
148138

149-
static ISentrySpan _startTimeToFullDisplaySpan(
150-
String routeName, ISentrySpan transaction, DateTime startTimestamp) {
151-
return transaction.startChild(SentryTraceOrigins.uiTimeToFullDisplay,
152-
description: '$routeName full display', startTimestamp: startTimestamp);
139+
ISentrySpan _createTTIDSpan(ISentrySpan transaction, String routeName, DateTime startTimestamp) {
140+
return transaction.startChild(
141+
SentryTraceOrigins.uiTimeToInitialDisplay,
142+
description: '$routeName initial display',
143+
startTimestamp: startTimestamp,
144+
);
153145
}
154146

155-
static void _endTimeToInitialDisplaySpan(ISentrySpan ttidSpan,
156-
ISentrySpan transaction, DateTime endTimestamp, int duration) async {
157-
transaction.setMeasurement('time_to_initial_display', duration,
158-
unit: DurationSentryMeasurementUnit.milliSecond);
159-
await ttidSpan.finish(endTimestamp: endTimestamp);
147+
ISentrySpan _createTTFDSpan(ISentrySpan transaction, String routeName, DateTime startTimestamp) {
148+
return transaction.startChild(
149+
SentryTraceOrigins.uiTimeToFullDisplay,
150+
description: '$routeName full display',
151+
startTimestamp: startTimestamp,
152+
);
160153
}
161154

162-
static void _endTimeToFullDisplaySpan(ISentrySpan ttfdSpan,
163-
ISentrySpan transaction, DateTime endTimestamp, int duration) async {
164-
transaction.setMeasurement('time_to_full_display', duration,
155+
void _finishSpan(ISentrySpan span, ISentrySpan transaction,
156+
String measurementName, int duration, DateTime endTimestamp) {
157+
transaction.setMeasurement(measurementName, duration,
165158
unit: DurationSentryMeasurementUnit.milliSecond);
166-
await ttfdSpan.finish(endTimestamp: endTimestamp);
159+
span.finish(endTimestamp: endTimestamp);
167160
}
168161
}

flutter/lib/src/sentry_widget.dart

-1
Original file line numberDiff line numberDiff line change
@@ -143,5 +143,4 @@ class SentryDisplayTracker {
143143
enum StrategyDecision {
144144
manual,
145145
approximation,
146-
undecided,
147146
}

0 commit comments

Comments
 (0)