-
-
Notifications
You must be signed in to change notification settings - Fork 237
/
time_to_initial_display_tracker.dart
136 lines (111 loc) · 3.87 KB
/
time_to_initial_display_tracker.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// ignore_for_file: invalid_use_of_internal_member
import 'dart:async';
import 'package:meta/meta.dart';
// ignore: implementation_imports
import 'package:sentry/src/sentry_tracer.dart';
import '../../sentry_flutter.dart';
import '../frame_callback_handler.dart';
@internal
class TimeToInitialDisplayTracker {
static final TimeToInitialDisplayTracker _instance =
TimeToInitialDisplayTracker._();
factory TimeToInitialDisplayTracker(
{FrameCallbackHandler? frameCallbackHandler}) {
if (frameCallbackHandler != null) {
_instance._frameCallbackHandler = frameCallbackHandler;
}
return _instance;
}
TimeToInitialDisplayTracker._();
FrameCallbackHandler _frameCallbackHandler = DefaultFrameCallbackHandler();
bool _isManual = false;
Completer<DateTime?>? _trackingCompleter;
DateTime? _endTimestamp;
final Duration _determineEndtimeTimeout = Duration(seconds: 5);
/// This endTimestamp is needed in the [TimeToFullDisplayTracker] class
@internal
DateTime? get endTimestamp => _endTimestamp;
Future<void> trackRegularRoute(
ISentrySpan transaction,
DateTime startTimestamp,
) async {
await _trackTimeToInitialDisplay(
transaction: transaction,
startTimestamp: startTimestamp,
);
}
Future<void> trackAppStart(ISentrySpan transaction,
{required DateTime startTimestamp,
required DateTime endTimestamp}) async {
await _trackTimeToInitialDisplay(
transaction: transaction,
startTimestamp: startTimestamp,
endTimestamp: endTimestamp,
origin: SentryTraceOrigins.autoUiTimeToDisplay,
);
// Store the end timestamp for potential use by TTFD tracking
_endTimestamp = endTimestamp;
}
Future<void> _trackTimeToInitialDisplay({
required ISentrySpan transaction,
required DateTime startTimestamp,
DateTime? endTimestamp,
String? origin,
}) async {
final _endTimestamp = endTimestamp ?? await determineEndTime();
if (_endTimestamp == null) return;
final tracer = transaction as SentryTracer;
final ttidSpan = transaction.startChild(
SentrySpanOperations.uiTimeToInitialDisplay,
description: '${tracer.name} initial display',
startTimestamp: startTimestamp,
);
ttidSpan.origin = origin ??
(_isManual
? SentryTraceOrigins.manualUiTimeToDisplay
: SentryTraceOrigins.autoUiTimeToDisplay);
final duration = Duration(
milliseconds: _endTimestamp.difference(startTimestamp).inMilliseconds);
final ttidMeasurement = SentryMeasurement.timeToInitialDisplay(duration);
transaction.setMeasurement(ttidMeasurement.name, ttidMeasurement.value,
unit: ttidMeasurement.unit);
await ttidSpan.finish(endTimestamp: _endTimestamp);
}
Future<DateTime?>? determineEndTime() {
_trackingCompleter = Completer<DateTime?>();
final future = _trackingCompleter?.future.timeout(
_determineEndtimeTimeout,
onTimeout: () {
return Future.value(null);
},
);
// If we already know it's manual we can return the future immediately
if (_isManual) {
return future;
}
// Schedules a check at the end of the frame to determine if the tracking
// should be completed immediately (approximation mode) or deferred (manual mode).
_frameCallbackHandler.addPostFrameCallback((_) {
if (!_isManual) {
completeTracking();
}
});
return future;
}
void markAsManual() {
_isManual = true;
}
void completeTracking() {
if (_trackingCompleter != null && !_trackingCompleter!.isCompleted) {
final endTimestamp = DateTime.now();
_endTimestamp = endTimestamp;
_trackingCompleter?.complete(endTimestamp);
}
}
void clear() {
_isManual = false;
_trackingCompleter = null;
// We can't clear the ttid end time stamp here, because it might be needed
// in the [TimeToFullDisplayTracker] class
}
}