Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
5ad308b
commit
buenaflor Apr 18, 2024
7bc756b
Merge branch 'main' into feat/app-start-improve
buenaflor Apr 23, 2024
3e0790a
Update
buenaflor Apr 23, 2024
d0eb540
Remove print
buenaflor Apr 23, 2024
05c208c
Remove comments
buenaflor Apr 23, 2024
c796b71
Update
buenaflor Apr 24, 2024
da8297e
Merge branch 'main' into feat/app-start-improve
buenaflor Apr 24, 2024
bdc8588
Add linting
buenaflor Apr 24, 2024
d1b37cd
Update CHANGELOG
buenaflor Apr 24, 2024
6f44478
Update CHANGELOG.md
buenaflor Apr 24, 2024
8051972
Update naming
buenaflor Apr 24, 2024
29671ff
Merge branch 'main' into feat/app-start-improve
buenaflor Apr 24, 2024
b401ed0
Update naming
buenaflor Apr 24, 2024
c06973c
Update naming
buenaflor Apr 24, 2024
2bb3887
Update description from first frame render to initial frame render
buenaflor Apr 24, 2024
3aadc19
Initial
buenaflor Apr 25, 2024
86bc3bf
Merge branch 'feat/app-start-improve' into feat/app-start-native-spans
buenaflor Apr 25, 2024
801483e
update
buenaflor Apr 26, 2024
d28af71
dart format
buenaflor Apr 26, 2024
21c61a6
Update comments
buenaflor Apr 26, 2024
88a8819
Update
buenaflor Apr 26, 2024
0d73052
Update
buenaflor Apr 26, 2024
1e05ae5
Update
buenaflor Apr 26, 2024
73d830a
Update
buenaflor Apr 26, 2024
62cb2be
Update
buenaflor Apr 26, 2024
111125c
Fix tests
buenaflor Apr 29, 2024
04ad023
Fix test
buenaflor Apr 29, 2024
e9e95c4
Add unused import
buenaflor Apr 29, 2024
bb785b6
Merge branch 'feat/app-start-improve' into feat/app-start-native-spans
buenaflor Apr 29, 2024
d0ce3a4
Fix tests
buenaflor Apr 29, 2024
b22a9c0
Update
buenaflor May 2, 2024
d4c6b1a
Updaet
buenaflor May 2, 2024
06325a2
Update
buenaflor May 3, 2024
bc7ba9e
Update
buenaflor May 3, 2024
4367089
Merge branch 'main' into feat/app-start-native-spans
buenaflor May 6, 2024
a847325
Update CHANGELOG
buenaflor May 6, 2024
7703c4e
Update CHANGELOG
buenaflor May 6, 2024
4ae3212
Update formatting of kotlin file
buenaflor May 6, 2024
342b518
Update
buenaflor May 6, 2024
7a1084f
Update test
buenaflor May 6, 2024
16303bd
format
buenaflor May 6, 2024
20e595f
Update SentryFlutterPlugin.kt
buenaflor May 6, 2024
0cf5d12
Update SentryFlutterPlugin.kt
buenaflor May 6, 2024
2820fc8
Update
buenaflor May 6, 2024
c122240
Update main.dart
buenaflor May 6, 2024
939ed17
Merge branch 'main' into feat/app-start-native-spans
buenaflor May 8, 2024
cdad9a8
Update
buenaflor May 8, 2024
ffb3695
Update
buenaflor May 8, 2024
e3197a7
Update
buenaflor May 8, 2024
427d516
Updaet
buenaflor May 8, 2024
ab63c3b
Format
buenaflor May 8, 2024
93b4516
Update flutter/lib/src/sentry_flutter.dart
buenaflor May 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
commit
  • Loading branch information
buenaflor committed Apr 18, 2024
commit 5ad308b273aab61e326be68c7f20151c6667d77c
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {

private var activity: WeakReference<Activity>? = null
private var framesTracker: ActivityFramesTracker? = null
private var engineEndTime: Long? = null

override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
engineEndTime = System.currentTimeMillis()

context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "sentry_flutter")
channel.setMethodCallHandler(this)
Expand Down Expand Up @@ -70,6 +73,7 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
"removeExtra" -> removeExtra(call.argument("key"), result)
"setTag" -> setTag(call.argument("key"), call.argument("value"), result)
"removeTag" -> removeTag(call.argument("key"), result)
"fetchEngineEndtime" -> fetchEngineEndtime(result)
else -> result.notImplemented()
}
}
Expand Down Expand Up @@ -142,7 +146,8 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}

val appStartTime = AppStartMetrics.getInstance().appStartTimeSpan.startTimestamp
val isColdStart = AppStartMetrics.getInstance().appStartType == AppStartMetrics.AppStartType.COLD
val isColdStart =
AppStartMetrics.getInstance().appStartType == AppStartMetrics.AppStartType.COLD

if (appStartTime == null) {
Log.w("Sentry", "App start won't be sent due to missing appStartTime")
Expand All @@ -157,6 +162,10 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
}
}

private fun fetchEngineEndtime(result: Result) {
result.success(engineEndTime)
}

private fun beginNativeFrames(result: Result) {
if (!sentryFlutter.autoPerformanceTracingEnabled) {
result.success(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class MainActivity : FlutterActivity() {

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
println("aaa configured engine")
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
_channel,
Expand Down
14 changes: 14 additions & 0 deletions flutter/ios/Classes/SentryFlutterPluginApple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
#endif
}

private static var engineEndtime: Int64 = 0

public static func register(with registrar: FlutterPluginRegistrar) {
let currentDate = Date() // Gets the current date and time
let timeInterval = currentDate.timeIntervalSince1970 // Time in seconds since epoch (1970-01-01)
engineEndtime = Int64(timeInterval * 1000) // Convert to milliseconds

#if os(iOS)
let channel = FlutterMethodChannel(name: "sentry_flutter", binaryMessenger: registrar.messenger())
#elseif os(macOS)
Expand Down Expand Up @@ -78,6 +84,11 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
return iso8601FormatterWithMillisecondPrecision.date(from: iso8601String)
?? iso8601Formatter.date(from: iso8601String) // Parse date with low precision formatter for backward compatible
}


private func fetchEngineEndtime(result: @escaping FlutterResult) {
result(SentryFlutterPluginApple.engineEndtime)
}

// swiftlint:disable:next cyclomatic_complexity
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
Expand Down Expand Up @@ -151,6 +162,9 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin {
let arguments = call.arguments as? [String: Any?]
let key = arguments?["key"] as? String
removeTag(key: key, result: result)

case "fetchEngineEndtime":
fetchEngineEndtime(result: result)

#if !os(tvOS) && !os(watchOS)
case "discardProfiler":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:sentry/sentry.dart';

import '../../sentry_flutter.dart';
import '../integrations/integrations.dart';
import '../native/sentry_native.dart';

Expand All @@ -25,6 +26,67 @@ class NativeAppStartEventProcessor implements EventProcessor {
event.measurements[measurement.name] = measurement;
_native.didAddAppStartMeasurement = true;
}

final op = 'app.start.${appStartInfo?.type.name}';

print('NativeAppStartEventProcessor.apply: ${event.tracer}');

final tracer = event.tracer;

// final spanParent = SentrySpan(
// tracer,
// SentrySpanContext(
// operation: op,
// description: 'Cold start',
// parentSpanId: tracer.context.spanId,
// traceId: tracer.context.traceId,
// ),
// Sentry.currentHub,
// startTimestamp: appStartInfo?.start);
//
// final span = SentrySpan(
// tracer,
// SentrySpanContext(
// operation: op,
// description: 'Engine init',
// parentSpanId: spanParent.context.spanId,
// traceId: tracer.context.traceId,
// ),
// Sentry.currentHub,
// startTimestamp: appStartInfo?.start);
// await span.finish(endTimestamp: appStartInfo?.engineEnd);
//
// final span2 = SentrySpan(
// tracer,
// SentrySpanContext(
// operation: op,
// description: 'Dart loading',
// parentSpanId: spanParent.context.spanId,
// traceId: tracer.context.traceId,
// ),
// Sentry.currentHub,
// startTimestamp: appStartInfo?.engineEnd);
// await span2.finish(endTimestamp: SentryFlutter.dartLoadingEnd);
//
// final span3 = SentrySpan(
// tracer,
// SentrySpanContext(
// operation: op,
// description: 'First frame loading',
// parentSpanId: spanParent.context.spanId,
// traceId: tracer.context.traceId,
// ),
// Sentry.currentHub,
// startTimestamp: SentryFlutter.dartLoadingEnd);
// await span3.finish(endTimestamp: appStartInfo?.end);
//
// await spanParent.finish(endTimestamp: appStartInfo?.end);
//
// tracer.children.add(spanParent);
// tracer.children.add(span);
// tracer.children.add(span2);
// tracer.children.add(span3);

return event;
}
}
72 changes: 68 additions & 4 deletions flutter/lib/src/integrations/native_app_start_integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
if (isIntegrationTest) {
final appStartInfo = AppStartInfo(AppStartType.cold,
start: DateTime.now(),
end: DateTime.now().add(const Duration(milliseconds: 100)));
end: DateTime.now().add(const Duration(milliseconds: 100)),
engineEnd: DateTime.now().add(const Duration(milliseconds: 50)));
setAppStartInfo(appStartInfo);
return;
}
Expand All @@ -69,14 +70,19 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
_native.appStartEnd ??= options.clock();
final appStartEnd = _native.appStartEnd;
final nativeAppStart = await _native.fetchNativeAppStart();
final engineEndtime = await _native.fetchEngineEndtime();

if (nativeAppStart == null || appStartEnd == null) {
if (nativeAppStart == null ||
appStartEnd == null ||
engineEndtime == null) {
return;
}

final appStartDateTime = DateTime.fromMillisecondsSinceEpoch(
nativeAppStart.appStartTime.toInt());
final duration = appStartEnd.difference(appStartDateTime);
final engineEndDatetime =
DateTime.fromMillisecondsSinceEpoch(engineEndtime);

// We filter out app start more than 60s.
// This could be due to many different reasons.
Expand All @@ -95,8 +101,63 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
nativeAppStart.isColdStart ? AppStartType.cold : AppStartType.warm,
start: DateTime.fromMillisecondsSinceEpoch(
nativeAppStart.appStartTime.toInt()),
end: appStartEnd);
end: appStartEnd,
engineEnd: engineEndDatetime);
setAppStartInfo(appStartInfo);

final routeName = SentryNavigatorObserver.currentRouteName;

// null means there is no navigator observer
if (routeName == null) {
final transaction = hub.startTransaction(
'App Start',
'ui.load',
startTimestamp: appStartInfo.start,
description: 'root /',
);

final op = 'app.start.${appStartInfo.type.name}';

final coldStartSpan = transaction.startChild(
op,
description: 'Cold start',
startTimestamp: appStartInfo.start,
);

final ttidSpan = transaction.startChild(
SentrySpanOperations.uiTimeToInitialDisplay,
description: 'Time to initial display',
startTimestamp: appStartInfo.start,
);

final engineInitSpan = coldStartSpan.startChild(
op,
description: 'Engine init and ready',
startTimestamp: appStartInfo.start,
);

final dartLoadingSpan = coldStartSpan.startChild(
op,
description: 'Dart isolate loading',
startTimestamp: appStartInfo.engineEnd,
);

final firstFrameRenderSpan = coldStartSpan.startChild(
op,
description: 'First frame render',
startTimestamp: SentryFlutter.dartLoadingEnd,
);

await engineInitSpan.finish(endTimestamp: appStartInfo.engineEnd);
await dartLoadingSpan.finish(endTimestamp: SentryFlutter.dartLoadingEnd);
await firstFrameRenderSpan.finish(endTimestamp: appStartInfo.end);
await coldStartSpan.finish(endTimestamp: appStartInfo.end);
await ttidSpan.finish(endTimestamp: appStartInfo.end);
await transaction.finish(endTimestamp: appStartInfo.end);
}

print('route name: ${SentryNavigatorObserver.currentRouteName}');

});
}

Expand All @@ -109,11 +170,14 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
enum AppStartType { cold, warm }

class AppStartInfo {
AppStartInfo(this.type, {required this.start, required this.end});
AppStartInfo(this.type,
{required this.start, required this.end, required this.engineEnd});

final AppStartType type;
final DateTime start;
final DateTime end;
final DateTime engineEnd;

Duration get duration => end.difference(start);

SentryMeasurement toMeasurement() {
Expand Down
4 changes: 4 additions & 0 deletions flutter/lib/src/native/sentry_native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class SentryNative {
return _invoke("fetchNativeAppStart", _binding.fetchNativeAppStart);
}

Future<int?> fetchEngineEndtime() async {
return _invoke("fetchEngineEndtime", _binding.fetchEngineEndtime);
}

// NativeFrames

Future<void> beginNativeFramesCollection() =>
Expand Down
2 changes: 2 additions & 0 deletions flutter/lib/src/native/sentry_native_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ abstract class SentryNativeBinding {

Future<NativeAppStart?> fetchNativeAppStart();

Future<int?> fetchEngineEndtime();

Future<void> beginNativeFrames();

Future<NativeFrames?> endNativeFrames(SentryId id);
Expand Down
5 changes: 5 additions & 0 deletions flutter/lib/src/native/sentry_native_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class SentryNativeChannel implements SentryNativeBinding {
return (json != null) ? NativeAppStart.fromJson(json) : null;
}

@override
Future<int?> fetchEngineEndtime() async {
return _channel.invokeMethod('fetchEngineEndtime');
}

@override
Future<void> beginNativeFrames() =>
_channel.invokeMethod('beginNativeFrames');
Expand Down
4 changes: 4 additions & 0 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ typedef FlutterOptionsConfiguration = FutureOr<void> Function(
mixin SentryFlutter {
static const _channel = MethodChannel('sentry_flutter');

static DateTime? dartLoadingEnd;

static Future<void> init(
FlutterOptionsConfiguration optionsConfiguration, {
AppRunner? appRunner,
Expand All @@ -43,6 +45,8 @@ mixin SentryFlutter {
}) async {
final flutterOptions = SentryFlutterOptions();

dartLoadingEnd = flutterOptions.clock();

if (platformChecker != null) {
flutterOptions.platformChecker = platformChecker;
}
Expand Down