From 362afada3b4e17ce4ea096d666fbcff8cc0e3b8e Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 29 Aug 2023 15:10:22 +0200 Subject: [PATCH 01/10] load contexts on android --- flutter/android/build.gradle | 2 +- .../io/sentry/flutter/SentryFlutterPlugin.kt | 24 ++++++++++++++++--- flutter/lib/src/sentry_flutter.dart | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index 1a0d0cf12b..5e40acda85 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -60,6 +60,6 @@ android { } dependencies { - api 'io.sentry:sentry-android:6.25.2' + api 'io.sentry:sentry-android:6.28.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 7035671ead..9827572214 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -10,15 +10,18 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.StandardMessageCodec import io.sentry.Breadcrumb +import io.sentry.DateUtils import io.sentry.HubAdapter +import io.sentry.Scope +import io.sentry.Sentry import io.sentry.SentryEvent import io.sentry.SentryLevel -import io.sentry.Sentry -import io.sentry.DateUtils import io.sentry.android.core.ActivityFramesTracker import io.sentry.android.core.AppStartState import io.sentry.android.core.BuildConfig.VERSION_NAME +import io.sentry.android.core.InternalSentrySdk import io.sentry.android.core.LoadClass import io.sentry.android.core.SentryAndroid import io.sentry.android.core.SentryAndroidOptions @@ -26,7 +29,6 @@ import io.sentry.protocol.DebugImage import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryId import io.sentry.protocol.User -import io.sentry.protocol.Geo import java.io.File import java.lang.ref.WeakReference import java.util.Locale @@ -68,6 +70,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) + "loadContexts" -> loadContexts(result) else -> result.notImplemented() } } @@ -436,6 +439,21 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } } } + + private fun loadContexts(result: Result) { + val options = HubAdapter.getInstance().options + if (options !is SentryAndroidOptions) { + result.success(null) + return + } + val currentScope = InternalSentrySdk.getCurrentScope() + val serializedScope = InternalSentrySdk.serializeScope( + context, + options, + currentScope + ) + result.success(serializedScope) + } } // Call the `completion` closure if cast to map value with `key` and type `T` is successful. diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 13aa7cc552..860990b213 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -154,7 +154,7 @@ mixin SentryFlutter { // Will enrich events with device context, native packages and integrations if (platformChecker.hasNativeIntegration && !platformChecker.isWeb && - (platform.isIOS || platform.isMacOS)) { + (platform.isIOS || platform.isMacOS || platform.isAndroid)) { integrations.add(LoadContextsIntegration(channel)); } From c6bd4793818ceeb5931774b4770f48355f10be23 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 29 Aug 2023 15:10:43 +0200 Subject: [PATCH 02/10] dont clear breadcrumbs on android --- dart/lib/src/sentry_client.dart | 38 ------ dart/test/sentry_client_test.dart | 195 ------------------------------ 2 files changed, 233 deletions(-) diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index 09f98e4d02..d7c09e3414 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -108,8 +108,6 @@ class SentryClient { return _sentryId; } - preparedEvent = _eventWithoutBreadcrumbsIfNeeded(preparedEvent); - var attachments = List.from(scope?.attachments ?? []); attachments.addAll(hint.attachments); var screenshot = hint.screenshot; @@ -319,8 +317,6 @@ class SentryClient { return _sentryId; } - preparedTransaction = _eventWithoutBreadcrumbsIfNeeded(preparedTransaction); - final attachments = scope?.attachments .where((element) => element.addToTransactions) .toList(); @@ -454,40 +450,6 @@ class SentryClient { _options.recorder.recordLostEvent(reason, category); } - T _eventWithoutBreadcrumbsIfNeeded(T event) { - if (_shouldRemoveBreadcrumbs(event)) { - return event.copyWith(breadcrumbs: []) as T; - } else { - return event; - } - } - - /// We do this to avoid duplicate breadcrumbs on Android as sentry-android applies the breadcrumbs - /// from the native scope onto every envelope sent through it. This scope will contain the breadcrumbs - /// sent through the scope sync feature. This causes duplicate breadcrumbs. - /// We then remove the breadcrumbs in all cases but if it is handled == false, - /// this is a signal that the app would crash and android would lose the breadcrumbs by the time the app is restarted to read - /// the envelope. - bool _shouldRemoveBreadcrumbs(SentryEvent event) { - if (_options.platformChecker.isWeb) { - return false; - } - - final isAndroid = _options.platformChecker.platform.isAndroid; - final enableScopeSync = _options.enableScopeSync; - - if (!isAndroid || !enableScopeSync) { - return false; - } - - final mechanisms = - (event.exceptions ?? []).map((e) => e.mechanism).whereType(); - final hasNoMechanism = mechanisms.isEmpty; - final hasOnlyHandledMechanism = - mechanisms.every((e) => (e.handled ?? true)); - return hasNoMechanism || hasOnlyHandledMechanism; - } - Future _attachClientReportsAndSend(SentryEnvelope envelope) { final clientReport = _options.recorder.flush(); envelope.addClientReport(clientReport); diff --git a/dart/test/sentry_client_test.dart b/dart/test/sentry_client_test.dart index 484dd009a5..d96929bd77 100644 --- a/dart/test/sentry_client_test.dart +++ b/dart/test/sentry_client_test.dart @@ -1152,201 +1152,6 @@ void main() { }); }); - group('Breadcrumbs', () { - late Fixture fixture; - - setUp(() { - fixture = Fixture(); - }); - - test('Clears breadcrumbs on Android for transaction', () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = - MockPlatformChecker(platform: MockPlatform.android()); - - final client = fixture.getSut(); - final transaction = SentryTransaction( - fixture.tracer, - breadcrumbs: [ - Breadcrumb(), - ], - ); - await client.captureTransaction(transaction); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedTransaction = - await transactionFromEnvelope(capturedEnvelope); - - expect((capturedTransaction['breadcrumbs'] ?? []).isEmpty, true); - }); - - test('Clears breadcrumbs on Android if mechanism.handled is true for event', - () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = - MockPlatformChecker(platform: MockPlatform.android()); - - final client = fixture.getSut(); - final event = SentryEvent(exceptions: [ - SentryException( - type: "type", - value: "value", - mechanism: Mechanism( - type: 'type', - handled: true, - ), - ) - ], breadcrumbs: [ - Breadcrumb() - ]); - await client.captureEvent(event); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedEvent = await eventFromEnvelope(capturedEnvelope); - - expect((capturedEvent.breadcrumbs ?? []).isEmpty, true); - }); - - test('Clears breadcrumbs on Android if mechanism.handled is null for event', - () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = - MockPlatformChecker(platform: MockPlatform.android()); - - final client = fixture.getSut(); - final event = SentryEvent(exceptions: [ - SentryException( - type: "type", - value: "value", - mechanism: Mechanism(type: 'type'), - ) - ], breadcrumbs: [ - Breadcrumb() - ]); - await client.captureEvent(event); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedEvent = await eventFromEnvelope(capturedEnvelope); - - expect((capturedEvent.breadcrumbs ?? []).isEmpty, true); - }); - - test('Clears breadcrumbs on Android if theres no mechanism for event', - () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = - MockPlatformChecker(platform: MockPlatform.android()); - - final client = fixture.getSut(); - final event = SentryEvent(exceptions: [ - SentryException( - type: "type", - value: "value", - ) - ], breadcrumbs: [ - Breadcrumb() - ]); - await client.captureEvent(event); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedEvent = await eventFromEnvelope(capturedEnvelope); - - expect((capturedEvent.breadcrumbs ?? []).isEmpty, true); - }); - - test( - 'Does not clear breadcrumbs on Android if mechanism.handled is false for event', - () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = - MockPlatformChecker(platform: MockPlatform.android()); - - final client = fixture.getSut(); - final event = SentryEvent(exceptions: [ - SentryException( - type: "type", - value: "value", - mechanism: Mechanism( - type: 'type', - handled: false, - ), - ) - ], breadcrumbs: [ - Breadcrumb() - ]); - await client.captureEvent(event); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedEvent = await eventFromEnvelope(capturedEnvelope); - - expect((capturedEvent.breadcrumbs ?? []).isNotEmpty, true); - }); - - test( - 'Does not clear breadcrumbs on Android if any mechanism.handled is false for event', - () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = - MockPlatformChecker(platform: MockPlatform.android()); - - final client = fixture.getSut(); - final event = SentryEvent(exceptions: [ - SentryException( - type: "type", - value: "value", - mechanism: Mechanism( - type: 'type', - handled: true, - ), - ), - SentryException( - type: "type", - value: "value", - mechanism: Mechanism( - type: 'type', - handled: false, - ), - ) - ], breadcrumbs: [ - Breadcrumb() - ]); - await client.captureEvent(event); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedEvent = await eventFromEnvelope(capturedEnvelope); - - expect((capturedEvent.breadcrumbs ?? []).isNotEmpty, true); - }); - - test('web breadcrumbs exist on web Android devices', () async { - fixture.options.enableScopeSync = true; - fixture.options.platformChecker = MockPlatformChecker( - platform: MockPlatform.android(), - isWebValue: true, - ); - - final client = fixture.getSut(); - final event = SentryEvent(exceptions: [ - SentryException( - type: "type", - value: "value", - mechanism: Mechanism( - type: 'type', - handled: true, - ), - ), - ], breadcrumbs: [ - Breadcrumb(), - ]); - await client.captureEvent(event); - - final capturedEnvelope = (fixture.transport).envelopes.first; - final capturedEvent = await eventFromEnvelope(capturedEnvelope); - - expect((capturedEvent.breadcrumbs ?? []).isNotEmpty, true); - }); - }); - group('ClientReportRecorder', () { late Fixture fixture; From 82c4cda37d4204e110c61e00b6e3656c4b863081 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Tue, 29 Aug 2023 15:50:46 +0200 Subject: [PATCH 03/10] use InternalSentrySdk.captureEnvelope in kotlin plugin --- .../io/sentry/flutter/SentryFlutterPlugin.kt | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 9827572214..a3778c61fd 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -100,18 +100,6 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { // Stub } - private fun writeEnvelope(envelope: ByteArray): Boolean { - val options = HubAdapter.getInstance().options - if (options.outboxPath.isNullOrEmpty()) { - return false - } - - val file = File(options.outboxPath, UUID.randomUUID().toString()) - file.writeBytes(envelope) - - return true - } - private fun initNativeSdk(call: MethodCall, result: Result) { if (!this::context.isInitialized) { result.error("1", "Context is null", null) @@ -361,20 +349,19 @@ class SentryFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { result.error("1", "The Sentry Android SDK is disabled", null) return } - - val args = call.arguments() as List? ?: listOf() + val args = call.arguments() as List? ?: listOf() if (args.isNotEmpty()) { val event = args.first() as ByteArray? - if (event != null && event.isNotEmpty()) { - if (!writeEnvelope(event)) { - result.error("2", "SentryOptions or outboxPath are null or empty", null) + val id = InternalSentrySdk.captureEnvelope(event) + if (id != null) { + result.success("") + } else { + result.error("2", "Failed to capture envelope", null) } - result.success("") return } } - result.error("3", "Envelope is null or empty", null) } From e884aba7e03ec56ab2dd7277b50f7dac2bec2680 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 13:09:56 +0200 Subject: [PATCH 04/10] remove unused import --- .../src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt index 651d20eee4..58423b91fa 100644 --- a/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt @@ -10,7 +10,6 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.StandardMessageCodec import io.sentry.Breadcrumb import io.sentry.DateUtils import io.sentry.Hint From e876f0dbd5df544ce49bd7a0529d90a9f3003154 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 13:24:25 +0200 Subject: [PATCH 05/10] add changelog entry, update tests --- CHANGELOG.md | 7 ++++++- flutter/test/sentry_flutter_test.dart | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c38892c7c2..6df8ee73f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,12 @@ - Do not leak extensions of external classes ([#1576](https://github.com/getsentry/sentry-dart/pull/1576)) - Make `hint` non-nullable in `BeforeSendCallback`, `BeforeBreadcrumbCall` and `EventProcessor` ([#1574](https://github.com/getsentry/sentry-dart/pull/1574)) - This will affect your callbacks, making this a breaking change. - + +### Features + +- Load Device Contexts from Sentry Java ([#1616](https://github.com/getsentry/sentry-dart/pull/1616)) + - Now the full device context is available in `BeforeSendCallback` + ## Unreleased ### Fixes diff --git a/flutter/test/sentry_flutter_test.dart b/flutter/test/sentry_flutter_test.dart index 56f6f250a6..9a7df7d063 100644 --- a/flutter/test/sentry_flutter_test.dart +++ b/flutter/test/sentry_flutter_test.dart @@ -27,9 +27,10 @@ final nonWebIntegrations = [ OnErrorIntegration, ]; -// These should only be added to Android +// These should be added to Android final androidIntegrations = [ LoadImageListIntegration, + LoadContextsIntegration, ]; // These should be added to iOS and macOS From 1b0cbd6f76106ac5c5a06ddf562c55336ac7f526 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 14:00:55 +0200 Subject: [PATCH 06/10] update documentation --- .../src/integrations/load_contexts_integration.dart | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/flutter/lib/src/integrations/load_contexts_integration.dart b/flutter/lib/src/integrations/load_contexts_integration.dart index f0aa565da3..8b9f3e686d 100644 --- a/flutter/lib/src/integrations/load_contexts_integration.dart +++ b/flutter/lib/src/integrations/load_contexts_integration.dart @@ -4,21 +4,16 @@ import 'package:flutter/services.dart'; import 'package:sentry/sentry.dart'; import '../sentry_flutter_options.dart'; -/// Load Device's Contexts from the iOS SDK. +/// Load Device's Contexts from the iOS & Android SDKs. /// -/// This integration calls the iOS SDK via Message channel to load the -/// Device's contexts before sending the event back to the iOS SDK via +/// This integration calls the iOS & Android SDKs via Message channel to load +/// the Device's contexts before sending the event back to the SDK via /// Message channel (already enriched with all the information). /// /// The Device's contexts are: /// App, Device and OS. /// -/// ps. This integration won't be run on Android because the Device's Contexts -/// is set on Android when the event is sent to the Android SDK via -/// the Message channel. -/// We intend to unify this behaviour in the future. -/// -/// This integration is only executed on iOS & MacOS Apps. +/// This integration is only executed on iOS, MacOS & Android Apps. class LoadContextsIntegration extends Integration { final MethodChannel _channel; From 0b54da317693476f6905c04a084bfa8ca47cc75d Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 14:01:43 +0200 Subject: [PATCH 07/10] update documentation --- flutter/lib/src/integrations/load_contexts_integration.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/src/integrations/load_contexts_integration.dart b/flutter/lib/src/integrations/load_contexts_integration.dart index 8b9f3e686d..7ac0626b3f 100644 --- a/flutter/lib/src/integrations/load_contexts_integration.dart +++ b/flutter/lib/src/integrations/load_contexts_integration.dart @@ -13,7 +13,7 @@ import '../sentry_flutter_options.dart'; /// The Device's contexts are: /// App, Device and OS. /// -/// This integration is only executed on iOS, MacOS & Android Apps. +/// This integration is only executed on iOS, macOS & Android Apps. class LoadContextsIntegration extends Integration { final MethodChannel _channel; From 7b589615dc9cd81fdbe45f2996958d08c50d5a47 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 14:02:44 +0200 Subject: [PATCH 08/10] update comment --- flutter/lib/src/integrations/load_contexts_integration.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/integrations/load_contexts_integration.dart b/flutter/lib/src/integrations/load_contexts_integration.dart index 7ac0626b3f..6395e134cf 100644 --- a/flutter/lib/src/integrations/load_contexts_integration.dart +++ b/flutter/lib/src/integrations/load_contexts_integration.dart @@ -189,8 +189,8 @@ class _LoadContextsIntegrationEventProcessor implements EventProcessor { event = event.copyWith(sdk: sdk); } - // on iOS, captureEnvelope does not call the beforeSend callback, - // hence we need to add these tags here. + // captureEnvelope does not call the beforeSend callback, hence we need to + // add these tags here. if (event.sdk?.name == 'sentry.dart.flutter') { final tags = event.tags ?? {}; tags['event.origin'] = 'flutter'; From 7b7b314551f48609382bd9ec345567e2a4dfec59 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 14:09:21 +0200 Subject: [PATCH 09/10] update changelog --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6df8ee73f6..ee955dac28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,11 +12,8 @@ - Do not leak extensions of external classes ([#1576](https://github.com/getsentry/sentry-dart/pull/1576)) - Make `hint` non-nullable in `BeforeSendCallback`, `BeforeBreadcrumbCall` and `EventProcessor` ([#1574](https://github.com/getsentry/sentry-dart/pull/1574)) - This will affect your callbacks, making this a breaking change. - -### Features - - Load Device Contexts from Sentry Java ([#1616](https://github.com/getsentry/sentry-dart/pull/1616)) - - Now the full device context is available in `BeforeSendCallback` + - Now the device context from Android is available in `BeforeSendCallback` ## Unreleased From f71e22a7392b81e85d625e50828751bdb0624cc5 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Mon, 4 Sep 2023 14:39:25 +0200 Subject: [PATCH 10/10] format --- sqflite/test/mocks/mocks.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sqflite/test/mocks/mocks.dart b/sqflite/test/mocks/mocks.dart index 5ecd57ce50..bcafb94302 100644 --- a/sqflite/test/mocks/mocks.dart +++ b/sqflite/test/mocks/mocks.dart @@ -33,7 +33,9 @@ ISentrySpan startTransactionShim( DatabaseExecutor, ], customMocks: [ - MockSpec(fallbackGenerators: {#startTransaction: startTransactionShim}), + MockSpec( + fallbackGenerators: {#startTransaction: startTransactionShim}, + ), ], ) void main() {}