Skip to content

Commit

Permalink
Merge branch 'main' into feat/app-start-improve-2
Browse files Browse the repository at this point in the history
  • Loading branch information
buenaflor committed May 8, 2024
2 parents 06836a2 + e0f6628 commit 8984d63
Show file tree
Hide file tree
Showing 31 changed files with 617 additions and 408 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
dart pub get
webdev build
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable'
with:
name: sentry
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dio.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
dart test -p vm --coverage=coverage --test-randomize-ordering-seed=random --chain-stack-traces
dart pub run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable'
with:
name: sentry_dio
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
cd drift
flutter test --coverage --test-randomize-ordering-seed=random
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable' && matrix.target == 'linux'
with:
name: sentry_drift
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/file.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
dart test -p vm --coverage=coverage --test-randomize-ordering-seed=random --chain-stack-traces
dart pub run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable'
with:
name: sentry_file
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/flutter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
flutter test --coverage --test-randomize-ordering-seed=random
dart run remove_from_coverage -f coverage/lcov.info -r 'binding.dart'
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable' && matrix.target == 'linux'
with:
name: sentry_flutter
Expand Down Expand Up @@ -188,7 +188,7 @@ jobs:
- uses: actions/checkout@v4

- name: ktlint
uses: ScaCap/action-ktlint@38262d0fb8bff43ddafc8b3c04bce6e6c7263319 # [email protected].1
uses: ScaCap/action-ktlint@5fdeb923cb8de70cb3ef2cfd853799d430946a5d # [email protected].2
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/hive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
dart test -p vm --coverage=coverage --test-randomize-ordering-seed=random --chain-stack-traces
dart pub run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable'
with:
name: sentry_hive
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/isar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
cd isar
flutter test -j 1 --coverage --test-randomize-ordering-seed=random
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable' && matrix.target == 'linux'
with:
name: sentry_isar
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/logging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
dart test -p vm --coverage=coverage --test-randomize-ordering-seed=random --chain-stack-traces
dart pub run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable'
with:
name: sentry_logging
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sqflite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
cd sqflite
flutter test --coverage --test-randomize-ordering-seed=random
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # pin@v3
- uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # pin@v3
if: runner.os == 'Linux' && matrix.sdk == 'stable' && matrix.target == 'linux'
with:
name: sentry_sqflite
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@

## Unreleased

### Fixes

- Event processor blocking transactions from being sent if `autoAppStart` is false ([#2028](https://github.com/getsentry/sentry-dart/pull/2028))

### Features

- Create app start transaction when no `SentryNavigatorObserver` is present ([#2017](https://github.com/getsentry/sentry-dart/pull/2017))
- Adds app start spans to first transaction ([#2009](https://github.com/getsentry/sentry-dart/pull/2009))

### Fixes

- Fix `PlatformException` title parsing ([#2033](https://github.com/getsentry/sentry-dart/pull/2033))

## 8.1.0

### Features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,14 @@ extension on JvmException {
String? module;
final typeParts = type?.split('.');
if (typeParts != null) {
if (typeParts.length > 1) {
if (typeParts.isNotEmpty) {
exceptionType = typeParts.last;
}
typeParts.remove(typeParts.last);
module = typeParts.join('.');

if (typeParts.isNotEmpty) {
module = typeParts.join('.');
}
}
final stackFrames = stackTrace.asMap().entries.map((entry) {
return entry.value.toSentryStackFrame(entry.key, nativePackageName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,25 @@ class NativeAppStartEventProcessor implements EventProcessor {

@override
Future<SentryEvent?> apply(SentryEvent event, Hint hint) async {
if (_native.didAddAppStartMeasurement || event is! SentryTransaction) {
final options = _hub.options;
if (_native.didAddAppStartMeasurement ||
event is! SentryTransaction ||
options is! SentryFlutterOptions) {
return event;
}

final appStartInfo = await NativeAppStartIntegration.getAppStartInfo();

final appStartEnd = _native.appStartEnd;
if (!options.autoAppStart) {
if (appStartEnd != null) {
appStartInfo?.end = appStartEnd;
} else {
// If autoAppStart is disabled and appStartEnd is not set, we can't add app starts
return event;
}
}

final measurement = appStartInfo?.toMeasurement();

if (measurement != null) {
Expand All @@ -44,6 +58,10 @@ class NativeAppStartEventProcessor implements EventProcessor {
Future<void> _attachAppStartSpans(
AppStartInfo appStartInfo, SentryTracer transaction) async {
final transactionTraceId = transaction.context.traceId;
final appStartEnd = appStartInfo.end;
if (appStartEnd == null) {
return;
}

final appStartSpan = await _createAndFinishSpan(
tracer: transaction,
Expand All @@ -52,7 +70,7 @@ class NativeAppStartEventProcessor implements EventProcessor {
parentSpanId: transaction.context.spanId,
traceId: transactionTraceId,
startTimestamp: appStartInfo.start,
endTimestamp: appStartInfo.end);
endTimestamp: appStartEnd);

final pluginRegistrationSpan = await _createAndFinishSpan(
tracer: transaction,
Expand All @@ -79,7 +97,7 @@ class NativeAppStartEventProcessor implements EventProcessor {
parentSpanId: appStartSpan.context.spanId,
traceId: transactionTraceId,
startTimestamp: appStartInfo.mainIsolateStart,
endTimestamp: appStartInfo.end);
endTimestamp: appStartEnd);

transaction.children.addAll([
appStartSpan,
Expand Down
135 changes: 75 additions & 60 deletions flutter/lib/src/integrations/native_app_start_integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
final SentryNative _native;
final FrameCallbackHandler _frameCallbackHandler;

/// Timeout duration to wait for the app start info to be fetched.
static const _timeoutDuration = Duration(seconds: 30);

/// We filter out App starts more than 60s
static const _maxAppStartMillis = 60000;

Expand All @@ -41,7 +44,8 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
if (_appStartInfo != null) {
return Future.value(_appStartInfo);
}
return _appStartCompleter.future;
return _appStartCompleter.future
.timeout(_timeoutDuration, onTimeout: () => null);
}

@visibleForTesting
Expand All @@ -64,30 +68,31 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
return;
}

if (options.autoAppStart) {
_frameCallbackHandler.addPostFrameCallback((timeStamp) async {
if (_native.didFetchAppStart) {
return;
}
if (_native.didFetchAppStart) {
return;
}

_frameCallbackHandler.addPostFrameCallback((timeStamp) async {
final nativeAppStart = await _native.fetchNativeAppStart();
if (nativeAppStart == null) {
setAppStartInfo(null);
return;
}

final mainIsolateStartDateTime = SentryFlutter.mainIsolateStartTime;
final appStartDateTime = DateTime.fromMillisecondsSinceEpoch(
nativeAppStart.appStartTime.toInt());
final pluginRegistrationDateTime = DateTime.fromMillisecondsSinceEpoch(
nativeAppStart.pluginRegistrationTime);
DateTime? appStartEndDateTime;

if (options.autoAppStart) {
print('in here: ${_native.appStartEnd}');

Check notice on line 90 in flutter/lib/src/integrations/native_app_start_integration.dart

View workflow job for this annotation

GitHub Actions / analyze / analyze

Don't invoke 'print' in production code.

Try using a logging framework. See https://dart.dev/lints/avoid_print to learn more about this problem.
// We only assign the current time if it's not already set - this is useful in tests
_native.appStartEnd ??= options.clock();
final appStartEndDateTime = _native.appStartEnd;
final nativeAppStart = await _native.fetchNativeAppStart();
final pluginRegistrationTime = nativeAppStart?.pluginRegistrationTime;
final mainIsolateStartDateTime = SentryFlutter.mainIsolateStartTime;

if (nativeAppStart == null ||
appStartEndDateTime == null ||
pluginRegistrationTime == null) {
return;
}
appStartEndDateTime = _native.appStartEnd;

final appStartDateTime = DateTime.fromMillisecondsSinceEpoch(
nativeAppStart.appStartTime.toInt());
final duration = appStartEndDateTime.difference(appStartDateTime);
final pluginRegistrationDateTime =
DateTime.fromMillisecondsSinceEpoch(pluginRegistrationTime);
final duration = appStartEndDateTime?.difference(appStartDateTime);

// We filter out app start more than 60s.
// This could be due to many different reasons.
Expand All @@ -97,42 +102,42 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
// If the system forked the process earlier to accelerate the app start.
// And some unknown reasons that could not be reproduced.
// We've seen app starts with hours, days and even months.
if (duration.inMilliseconds > _maxAppStartMillis) {
if (duration != null && duration.inMilliseconds > _maxAppStartMillis) {
setAppStartInfo(null);
return;
}
}

final appStartInfo = AppStartInfo(
nativeAppStart.isColdStart ? AppStartType.cold : AppStartType.warm,
start: appStartDateTime,
end: appStartEndDateTime,
pluginRegistration: pluginRegistrationDateTime,
mainIsolateStart: mainIsolateStartDateTime);

setAppStartInfo(appStartInfo);

// When we don't have a SentryNavigatorObserver, a TTID transaction
// is not created therefore we need to create a transaction ourselves.
// We detect this by checking if the currentRouteName is null.
// This is a workaround since there is no api that tells us if
// the navigator observer exists or not. The currentRouteName is always
// set during a didPush triggered by the navigator observer.
if (SentryNavigatorObserver.currentRouteName == null) {
const screenName = SentryNavigatorObserver.rootScreenName;
final transaction = hub.startTransaction(
screenName, SentrySpanOperations.uiLoad,
startTimestamp: appStartInfo.start);
final ttidSpan = transaction.startChild(
SentrySpanOperations.uiTimeToInitialDisplay,
description: '$screenName initial display',
startTimestamp: appStartInfo.start);
await ttidSpan.finish(endTimestamp: appStartInfo.end);
await transaction.finish(endTimestamp: appStartInfo.end);
}
});
}
final appStartInfo = AppStartInfo(
nativeAppStart.isColdStart ? AppStartType.cold : AppStartType.warm,
start: appStartDateTime,
end: appStartEndDateTime,
pluginRegistration: pluginRegistrationDateTime,
mainIsolateStart: mainIsolateStartDateTime);

setAppStartInfo(appStartInfo);

options.addEventProcessor(NativeAppStartEventProcessor(_native));
// When we don't have a SentryNavigatorObserver, a TTID transaction
// is not created therefore we need to create a transaction ourselves.
// We detect this by checking if the currentRouteName is null.
// This is a workaround since there is no api that tells us if
// the navigator observer exists or not. The currentRouteName is always
// set during a didPush triggered by the navigator observer.
if (SentryNavigatorObserver.currentRouteName == null) {
const screenName = SentryNavigatorObserver.rootScreenName;
final transaction = hub.startTransaction(
screenName, SentrySpanOperations.uiLoad,
startTimestamp: appStartInfo.start);
final ttidSpan = transaction.startChild(
SentrySpanOperations.uiTimeToInitialDisplay,
description: '$screenName initial display',
startTimestamp: appStartInfo.start);
await ttidSpan.finish(endTimestamp: appStartInfo.end);
await transaction.finish(endTimestamp: appStartInfo.end);
}
});

options.addEventProcessor(NativeAppStartEventProcessor(_native, hub: hub));

options.sdk.addIntegration('nativeAppStartIntegration');
}
Expand All @@ -141,21 +146,31 @@ class NativeAppStartIntegration extends Integration<SentryFlutterOptions> {
enum AppStartType { cold, warm }

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

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

// We allow the end to be null, since it might be set at a later time
// with setAppStartEnd when autoAppStart is disabled
DateTime? end;

final DateTime pluginRegistration;
final DateTime mainIsolateStart;

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

SentryMeasurement toMeasurement() {
SentryMeasurement? toMeasurement() {
final duration = this.duration;
if (duration == null) {
return null;
}
return type == AppStartType.cold
? SentryMeasurement.coldAppStart(duration)
: SentryMeasurement.warmAppStart(duration);
Expand Down
Loading

0 comments on commit 8984d63

Please sign in to comment.