Skip to content

Commit 8825ed8

Browse files
authored
Use FFI/JNI for captureEnvelope on iOS and Android (#3115)
* base impl for JNI * base impl for cocoa -> maybe improve perf * update * update * update * update * update * Remove method channels for captureEnvelope * Update * Update * Update * Update * Update * Update * Update * Update * Update * Update sentry_native_channel_test.dart * Update * Update * Update * Update * Update * Update test * Update test * Update CHANGELOG * Update CHANGELOG * Update native bindings * Test not verbose * fix test * Update * Update * Update * Update tests * Fix analyze * Fix tests * Fix tests * Fix linux tests * Fix imports * Ignore coverage for ffi/jni captureEnvelope paths * Fix potential memory leak * Update * Fix * Fix * Update * Analyze * Revert github workflow * Update * Update
1 parent 5b9a0da commit 8825ed8

File tree

10 files changed

+414
-68
lines changed

10 files changed

+414
-68
lines changed

.github/actions/flutter-test/action.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ runs:
2121
distribution: 'adopt'
2222

2323
# Install required dependencies for Flutter on Linux on Ubuntu
24-
- name: "Setup Linux"
24+
- name: 'Setup Linux'
2525
if: matrix.target == 'linux'
2626
shell: bash
2727
run: |
@@ -44,11 +44,15 @@ runs:
4444
else
4545
testCmd="flutter test --test-randomize-ordering-seed=random"
4646
fi
47-
47+
4848
if ${{ (matrix.target == 'linux' && matrix.sdk == 'stable' && 'true') || 'false' }} ; then
4949
$testCmd --coverage
5050
if [[ "$INPUT_DIRECTORY" == 'packages/flutter' ]]; then
51+
# Remove native binding/bridge files from coverage.
52+
# These FFI/JNI bindings are currently not unit tested due to limitations of FFI/JNI mocking.
5153
dart run remove_from_coverage -f coverage/lcov.info -r 'binding.dart'
54+
dart run remove_from_coverage -f coverage/lcov.info -r 'lib/src/native/java/sentry_native_java.dart'
55+
dart run remove_from_coverage -f coverage/lcov.info -r 'lib/src/native/cocoa/sentry_native_cocoa.dart'
5256
fi
5357
else
5458
$testCmd

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
- Add `DioException` response data to error breadcrumb ([#3164](https://github.com/getsentry/sentry-dart/pull/3164))
1818
- Bumped `dio` min verion to `5.2.0`
19+
- Use FFI/JNI for `captureEnvelope` on iOS and Android ([#3115](https://github.com/getsentry/sentry-dart/pull/3115))
1920
- Log a warning when dropping envelope items ([#3165](https://github.com/getsentry/sentry-dart/pull/3165))
2021
- Call options.log for structured logs ([#3187](https://github.com/getsentry/sentry-dart/pull/3187))
2122
- Tag all spans during app start with start type info ([#3190](https://github.com/getsentry/sentry-dart/pull/3190))

packages/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ class SentryFlutterPlugin :
6363
) {
6464
when (call.method) {
6565
"initNativeSdk" -> initNativeSdk(call, result)
66-
"captureEnvelope" -> captureEnvelope(call, result)
6766
"loadImageList" -> loadImageList(call, result)
6867
"closeNativeSdk" -> closeNativeSdk(result)
6968
"fetchNativeAppStart" -> fetchNativeAppStart(result)
@@ -368,32 +367,6 @@ class SentryFlutterPlugin :
368367

369368
result.success("")
370369
}
371-
372-
private fun captureEnvelope(
373-
call: MethodCall,
374-
result: Result,
375-
) {
376-
if (!Sentry.isEnabled()) {
377-
result.error("1", "The Sentry Android SDK is disabled", null)
378-
return
379-
}
380-
val args = call.arguments() as List<Any>? ?: listOf()
381-
if (args.isNotEmpty()) {
382-
val event = args.first() as ByteArray?
383-
val containsUnhandledException = args[1] as Boolean
384-
if (event != null && event.isNotEmpty()) {
385-
val id = InternalSentrySdk.captureEnvelope(event, containsUnhandledException)
386-
if (id != null) {
387-
result.success("")
388-
} else {
389-
result.error("2", "Failed to capture envelope", null)
390-
}
391-
return
392-
}
393-
}
394-
result.error("3", "Envelope is null or empty", null)
395-
}
396-
397370
private fun loadImageList(
398371
call: MethodCall,
399372
result: Result,

packages/flutter/ffi-jni.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ output:
1313
log_level: all
1414

1515
classes:
16+
- io.sentry.android.core.InternalSentrySdk
1617
- io.sentry.android.replay.ReplayIntegration
1718
- io.sentry.flutter.SentryFlutterPlugin
1819
- android.graphics.Bitmap

packages/flutter/ios/sentry_flutter/Sources/sentry_flutter/SentryFlutterPlugin.swift

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
8181
case "closeNativeSdk":
8282
closeNativeSdk(call, result: result)
8383

84-
case "captureEnvelope":
85-
captureEnvelope(call, result: result)
86-
8784
case "fetchNativeAppStart":
8885
fetchNativeAppStart(result: result)
8986

@@ -412,24 +409,6 @@ public class SentryFlutterPlugin: NSObject, FlutterPlugin {
412409
return !name.isEmpty
413410
}
414411

415-
private func captureEnvelope(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
416-
guard let arguments = call.arguments as? [Any],
417-
!arguments.isEmpty,
418-
let data = (arguments.first as? FlutterStandardTypedData)?.data else {
419-
print("Envelope is null or empty!")
420-
result(FlutterError(code: "2", message: "Envelope is null or empty", details: nil))
421-
return
422-
}
423-
guard let envelope = PrivateSentrySDKOnly.envelope(with: data) else {
424-
print("Cannot parse the envelope data")
425-
result(FlutterError(code: "3", message: "Cannot parse the envelope data", details: nil))
426-
return
427-
}
428-
PrivateSentrySDKOnly.capture(envelope)
429-
result("")
430-
return
431-
}
432-
433412
struct TimeSpan {
434413
var startTimestampMsSinceEpoch: NSNumber
435414
var stopTimestampMsSinceEpoch: NSNumber

packages/flutter/lib/src/native/cocoa/sentry_native_cocoa.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:async';
2+
import 'dart:typed_data';
23
import 'package:meta/meta.dart';
34
import 'package:objective_c/objective_c.dart';
45

@@ -50,6 +51,30 @@ class SentryNativeCocoa extends SentryNativeChannel {
5051
return super.init(hub);
5152
}
5253

54+
// coverage: ignore-start
55+
@override
56+
FutureOr<void> captureEnvelope(
57+
Uint8List envelopeData, bool containsUnhandledException) {
58+
try {
59+
final nsData = envelopeData.toNSData();
60+
final envelope = cocoa.PrivateSentrySDKOnly.envelopeWithData(nsData);
61+
if (envelope != null) {
62+
cocoa.PrivateSentrySDKOnly.captureEnvelope(envelope);
63+
} else {
64+
options.log(
65+
SentryLevel.error, 'Failed to capture envelope: envelope is null');
66+
}
67+
} catch (exception, stackTrace) {
68+
options.log(SentryLevel.error, 'Failed to capture envelope',
69+
exception: exception, stackTrace: stackTrace);
70+
71+
if (options.automatedTestMode) {
72+
rethrow;
73+
}
74+
}
75+
}
76+
// coverage: ignore-end
77+
5378
@override
5479
FutureOr<void> setReplayConfig(ReplayConfig config) {
5580
// Note: unused on iOS.

0 commit comments

Comments
 (0)