Skip to content

Commit

Permalink
draft impl for ttid
Browse files Browse the repository at this point in the history
  • Loading branch information
buenaflor committed Feb 2, 2024
1 parent 83626bd commit 1dbc007
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 20 deletions.
1 change: 1 addition & 0 deletions flutter/example/lib/auto_close_screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:sentry/sentry.dart';
import 'package:sentry_flutter/sentry_flutter.dart';

/// This screen is only used to demonstrate how route navigation works.
/// Init will create a child span and pop the screen after 3 seconds.
Expand Down
7 changes: 4 additions & 3 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -723,9 +723,10 @@ void navigateToAutoCloseScreen(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
settings: const RouteSettings(name: 'AutoCloseScreen'),
builder: (context) => const SentryDisplayWidget(child: AutoCloseScreen(),
)),
settings: const RouteSettings(name: 'AutoCloseScreen'),
builder: (context) => const SentryDisplayWidget(
child: AutoCloseScreen(),
)),
);
}

Expand Down
74 changes: 64 additions & 10 deletions flutter/lib/src/navigation/sentry_navigator_observer.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

Expand Down Expand Up @@ -86,13 +87,18 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
final RouteNameExtractor? _routeNameExtractor;
final AdditionalInfoExtractor? _additionalInfoProvider;
final SentryNative? _native;
static ISentrySpan? _transaction2;

static ISentrySpan? get transaction2 => _transaction2;

ISentrySpan? _transaction;

static String? _currentRouteName;

@internal
static String? get currentRouteName => _currentRouteName;
static var startTime = DateTime.now();
static ISentrySpan? ttidSpan;

@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
Expand All @@ -108,7 +114,36 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
);

_finishTransaction();

var routeName = route.settings.name ?? 'Unknown';

_startTransaction(route);

// Start timing
DateTime? approximationEndTimestamp;
int? approximationDurationMillis;

SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
approximationEndTimestamp = DateTime.now();
approximationDurationMillis =
approximationEndTimestamp!.millisecond - startTime.millisecond;
});

SentryDisplayTracker().startTimeout(routeName, () {
_transaction2?.setMeasurement(
'time_to_initial_display', approximationDurationMillis!,
unit: DurationSentryMeasurementUnit.milliSecond);
ttidSpan?.setTag('measurement', 'approximation');
ttidSpan?.finish(endTimestamp: approximationEndTimestamp!);
});
}

void freezeUIForSeconds(int seconds) {
var sw = Stopwatch()..start();
while (sw.elapsed.inSeconds < seconds) {
// This loop will block the UI thread.
}
sw.stop();
}

@override
Expand Down Expand Up @@ -193,16 +228,26 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {
if (name == '/') {
name = 'root ("/")';
}
final transactionContext = SentryTransactionContext(
// final transactionContext = SentryTransactionContext(
// name,
// 'navigation',
// transactionNameSource: SentryTransactionNameSource.component,
// // ignore: invalid_use_of_internal_member
// origin: SentryTraceOrigins.autoNavigationRouteObserver,
// );

final transactionContext2 = SentryTransactionContext(
name,
'navigation',
'ui.load',
transactionNameSource: SentryTransactionNameSource.component,
// ignore: invalid_use_of_internal_member
origin: SentryTraceOrigins.autoNavigationRouteObserver,
);

_transaction = _hub.startTransactionWithContext(
transactionContext,
// 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,
autoFinishAfter: _autoFinishAfter,
trimEnd: true,
Expand All @@ -225,24 +270,33 @@ class SentryNavigatorObserver extends RouteObserver<PageRoute<dynamic>> {

// if _enableAutoTransactions is enabled but there's no traces sample rate
if (_transaction is NoOpSentrySpan) {
_transaction = null;
_transaction2 = null;
return;
}

startTime = DateTime.now();
ttidSpan = _transaction2?.startChild('ui.load.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');

if (arguments != null) {
_transaction?.setData('route_settings_arguments', arguments);
_transaction2?.setData('route_settings_arguments', arguments);
}

await _hub.configureScope((scope) {
scope.span ??= _transaction;
scope.span ??= _transaction2;
});

await _native?.beginNativeFramesCollection();
}

Future<void> _finishTransaction() async {
_transaction?.status ??= SpanStatus.ok();
await _transaction?.finish();
Future<void> _finishTransaction({DateTime? endTimestamp}) async {
_transaction2?.status ??= SpanStatus.ok();
await _transaction2?.finish(endTimestamp: endTimestamp);
}
}

Expand Down
18 changes: 13 additions & 5 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,21 @@ mixin SentryFlutter {
options.sdk = sdk;
}

static void reportInitialDisplay() {
print('reported accurate TTID!');
static void reportInitialDisplay(BuildContext context) {
final routeName = ModalRoute.of(context)?.settings.name ?? 'Unknown';
final endTime = DateTime.now();
if (!SentryDisplayTracker().reportManual(routeName)) {
SentryNavigatorObserver.transaction2?.setMeasurement(
'time_to_initial_display',
endTime.millisecond - SentryNavigatorObserver.startTime.millisecond,
unit: DurationSentryMeasurementUnit.milliSecond);

SentryNavigatorObserver.ttidSpan?.setTag('measurement', 'manual');
SentryNavigatorObserver.ttidSpan?.finish(endTimestamp: endTime);
}
}

static void reportFullDisplay() {

}
static void reportFullDisplay() {}

@internal
static SentryNative? get native => _native;
Expand Down
45 changes: 43 additions & 2 deletions flutter/lib/src/sentry_widget.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../sentry_flutter.dart';

/// This widget serves as a wrapper to include Sentry widgets such
Expand Down Expand Up @@ -34,13 +37,51 @@ class SentryDisplayWidget extends StatefulWidget {
class _SentryDisplayWidgetState extends State<SentryDisplayWidget> {
@override
void initState() {
// TODO: implement initState
super.initState();
SentryFlutter.reportInitialDisplay();
WidgetsBinding.instance.addPostFrameCallback((_) {
SentryFlutter.reportInitialDisplay(context);
});
}

@override
Widget build(BuildContext context) {
return widget.child;
}
}

class SentryDisplayTracker {
static final SentryDisplayTracker _instance =
SentryDisplayTracker._internal();

factory SentryDisplayTracker() {
return _instance;
}

SentryDisplayTracker._internal();

final Map<String, bool> _manualReportReceived = {};
final Map<String, Timer> _timers = {};

void startTimeout(String routeName, Function onTimeout) {
_timers[routeName]?.cancel(); // Cancel any existing timer
_timers[routeName] = Timer(Duration(seconds: 2), () {
// Don't send if we already received a manual report or if we're on the root route e.g App start.
if (!(_manualReportReceived[routeName] ?? false)) {
onTimeout();
}
});
}

bool reportManual(String routeName) {
var wasReportedAlready = _manualReportReceived[routeName] ?? false;
_manualReportReceived[routeName] = true;
_timers[routeName]?.cancel();
return wasReportedAlready;
}

void clearState(String routeName) {
_manualReportReceived.remove(routeName);
_timers[routeName]?.cancel();
_timers.remove(routeName);
}
}

0 comments on commit 1dbc007

Please sign in to comment.