From 1bb772cb428318eebe18494015a68c6acc1907e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20Andra=C5=A1ec?= Date: Thu, 27 Jul 2023 08:05:09 +0000 Subject: [PATCH] Mark exceptions not handled by user as `handled: false` (#1535) --- .../workflows/flutter_integration_test.yml | 64 +++--- CHANGELOG.md | 14 +- dart/lib/sentry.dart | 2 + .../src/run_zoned_guarded_integration.dart | 4 +- dart/lib/src/sentry_isolate.dart | 5 +- dart/lib/src/sentry_options.dart | 1 - dart/lib/src/utils.dart | 5 +- dart/lib/src/version.dart | 2 +- dart/pubspec.yaml | 2 +- dart/test/protocol/breadcrumb_test.dart | 1 - dart/test/sentry_envelope_header_test.dart | 1 - dart/test/sentry_envelope_item_test.dart | 1 - dart/test/sentry_envelope_test.dart | 1 - dart/test/sentry_event_test.dart | 1 - dart/test/sentry_span_test.dart | 1 - dart/test/sentry_tracer_test.dart | 1 - dio/lib/src/dio_event_processor.dart | 72 ++++++- dio/lib/src/version.dart | 2 +- dio/pubspec.yaml | 4 +- dio/test/dio_event_processor_test.dart | 201 +++++++++++++++++- file/lib/src/version.dart | 2 +- file/pubspec.yaml | 4 +- flutter/example/lib/main.dart | 1 - flutter/example/pubspec.yaml | 2 +- .../Classes/SentryFlutterPluginApple.swift | 4 + .../flutter_error_integration.dart | 7 +- .../integrations/native_sdk_integration.dart | 2 + flutter/lib/src/sentry_flutter_options.dart | 10 +- flutter/lib/src/version.dart | 2 +- flutter/pubspec.yaml | 4 +- .../flutter_error_integration_test.dart | 6 +- .../init_native_sdk_integration_test.dart | 5 +- logging/lib/src/version.dart | 2 +- logging/pubspec.yaml | 4 +- sqflite/lib/src/version.dart | 2 +- sqflite/pubspec.yaml | 4 +- 36 files changed, 366 insertions(+), 80 deletions(-) diff --git a/.github/workflows/flutter_integration_test.yml b/.github/workflows/flutter_integration_test.yml index beb70244cd..761d2771e5 100644 --- a/.github/workflows/flutter_integration_test.yml +++ b/.github/workflows/flutter_integration_test.yml @@ -46,42 +46,42 @@ jobs: - name: flutter pub get run: flutter pub get - - name: Gradle cache - uses: gradle/gradle-build-action@v2 + - name: Gradle cache + uses: gradle/gradle-build-action@v2 - - name: AVD cache - uses: actions/cache@v3 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* + - name: AVD cache + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* key: avd-21 - - name: create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 - with: - working-directory: ./flutter/example - api-level: 21 - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - arch: x86_64 - profile: Nexus 6 - script: echo "Generated AVD snapshot for caching." + - name: create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 + with: + working-directory: ./flutter/example + api-level: 21 + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + arch: x86_64 + profile: Nexus 6 + script: echo "Generated AVD snapshot for caching." - - name: launch android emulator & run android integration test - uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 - with: - working-directory: ./flutter/example - api-level: 21 - force-avd-creation: false - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: true - arch: x86_64 - profile: Nexus 6 - script: flutter test integration_test/integration_test.dart --verbose + - name: launch android emulator & run android integration test + uses: reactivecircus/android-emulator-runner@d94c3fbe4fe6a29e4a5ba47c12fb47677c73656b #pin@v2.28.0 + with: + working-directory: ./flutter/example + api-level: 21 + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: true + arch: x86_64 + profile: Nexus 6 + script: flutter test integration_test/integration_test.dart --verbose test-ios: runs-on: macos-13 diff --git a/CHANGELOG.md b/CHANGELOG.md index e558039d62..48ec8862bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,25 @@ # Changelog -## Unreleased +## 8.0.0 +### Breaking Changes + +- Mark exceptions not handled by the user as `handled: false` ([#1535](https://github.com/getsentry/sentry-dart/pull/1535)) + - This will affect your release health data, and is therefore considered a breaking change. + +## 7.9.0 + ### Features - Send trace origin ([#1534](https://github.com/getsentry/sentry-dart/pull/1534)) [Trace origin](https://develop.sentry.dev/sdk/performance/trace-origin/) indicates what created a trace or a span. Not all transactions and spans contain enough information to tell whether the user or what precisely in the SDK created it. Origin solves this problem. The SDK now sends origin for transactions and spans. +- Add `appHangTimeoutInterval` to `SentryFlutterOptions` ([#1568](https://github.com/getsentry/sentry-dart/pull/1568)) +- DioEventProcessor: Append http response body ([#1557](https://github.com/getsentry/sentry-dart/pull/1557)) + - This is opt-in and depends on `maxResponseBodySize` + - Only for `dio` package + ### Dependencies - Bump Cocoa SDK from v8.8.0 to v8.9.1 ([#1553](https://github.com/getsentry/sentry-dart/pull/1553)) diff --git a/dart/lib/sentry.dart b/dart/lib/sentry.dart index f23525bfda..9d06bb7e2a 100644 --- a/dart/lib/sentry.dart +++ b/dart/lib/sentry.dart @@ -46,3 +46,5 @@ export 'src/utils/url_details.dart'; export 'src/utils/http_header_utils.dart'; // ignore: invalid_export_of_internal_element export 'src/sentry_trace_origins.dart'; +// ignore: invalid_export_of_internal_element +export 'src/utils.dart'; diff --git a/dart/lib/src/run_zoned_guarded_integration.dart b/dart/lib/src/run_zoned_guarded_integration.dart index ba70fc2472..623a9a5f54 100644 --- a/dart/lib/src/run_zoned_guarded_integration.dart +++ b/dart/lib/src/run_zoned_guarded_integration.dart @@ -44,8 +44,8 @@ class RunZonedGuardedIntegration extends Integration { stackTrace: stackTrace, ); - // runZonedGuarded doesn't crash the App. - final mechanism = Mechanism(type: 'runZonedGuarded', handled: true); + // runZonedGuarded doesn't crash the app, but is not handled by the user. + final mechanism = Mechanism(type: 'runZonedGuarded', handled: false); final throwableMechanism = ThrowableMechanism(mechanism, exception); final event = SentryEvent( diff --git a/dart/lib/src/sentry_isolate.dart b/dart/lib/src/sentry_isolate.dart index 898904cd27..8a6b71d48f 100644 --- a/dart/lib/src/sentry_isolate.dart +++ b/dart/lib/src/sentry_isolate.dart @@ -69,9 +69,10 @@ class SentryIsolate { stackTrace == null ? null : StackTrace.fromString(stackTrace), ); - // Isolate errors don't crash the App. - final mechanism = Mechanism(type: 'isolateError', handled: true); + // Isolate errors don't crash the app, but is not handled by the user. + final mechanism = Mechanism(type: 'isolateError', handled: false); final throwableMechanism = ThrowableMechanism(mechanism, throwable); + final event = SentryEvent( throwable: throwableMechanism, level: SentryLevel.fatal, diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 34544076ba..55450be9dd 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -13,7 +13,6 @@ import 'diagnostic_logger.dart'; import 'environment/environment_variables.dart'; import 'noop_client.dart'; import 'transport/noop_transport.dart'; -import 'utils.dart'; import 'version.dart'; // TODO: shutdownTimeout, flushTimeoutMillis diff --git a/dart/lib/src/utils.dart b/dart/lib/src/utils.dart index f349635051..db5ca614c2 100644 --- a/dart/lib/src/utils.dart +++ b/dart/lib/src/utils.dart @@ -8,9 +8,11 @@ import 'package:meta/meta.dart'; /// Sentry does not take a timezone and instead expects the date-time to be /// submitted in UTC timezone. +@internal DateTime getUtcDateTime() => DateTime.now().toUtc(); /// Formats a Date as ISO8601 and UTC with millis precision +@internal String formatDateAsIso8601WithMillisPrecision(DateTime date) { var iso = date.toIso8601String(); final millisecondSeparatorIndex = iso.lastIndexOf('.'); @@ -22,9 +24,10 @@ String formatDateAsIso8601WithMillisPrecision(DateTime date) { return '${iso}Z'; } +@internal final utf8JsonEncoder = JsonUtf8Encoder(null, jsonSerializationFallback, null); -@visibleForTesting +@internal Object? jsonSerializationFallback(Object? nonEncodable) { if (nonEncodable == null) { return null; diff --git a/dart/lib/src/version.dart b/dart/lib/src/version.dart index eea097529a..6e9ff2059d 100644 --- a/dart/lib/src/version.dart +++ b/dart/lib/src/version.dart @@ -9,7 +9,7 @@ library version; /// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '7.8.0'; +const String sdkVersion = '7.9.0'; String sdkName(bool isWeb) => isWeb ? _browserSdkName : _ioSdkName; diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml index b73eaf94ac..330d6084e7 100644 --- a/dart/pubspec.yaml +++ b/dart/pubspec.yaml @@ -1,5 +1,5 @@ name: sentry -version: 7.8.0 +version: 7.9.0 description: > A crash reporting library for Dart that sends crash reports to Sentry.io. This library supports Dart VM and Web. For Flutter consider sentry_flutter instead. diff --git a/dart/test/protocol/breadcrumb_test.dart b/dart/test/protocol/breadcrumb_test.dart index 4062e1b895..ddd4008d98 100644 --- a/dart/test/protocol/breadcrumb_test.dart +++ b/dart/test/protocol/breadcrumb_test.dart @@ -1,7 +1,6 @@ import 'package:collection/collection.dart'; import 'package:sentry/sentry.dart'; import 'package:test/test.dart'; -import 'package:sentry/src/utils.dart'; void main() { final timestamp = DateTime.now(); diff --git a/dart/test/sentry_envelope_header_test.dart b/dart/test/sentry_envelope_header_test.dart index 5dff84aa87..cc10f97434 100644 --- a/dart/test/sentry_envelope_header_test.dart +++ b/dart/test/sentry_envelope_header_test.dart @@ -1,6 +1,5 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_envelope_header.dart'; -import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks.dart'; diff --git a/dart/test/sentry_envelope_item_test.dart b/dart/test/sentry_envelope_item_test.dart index 4fe01e5a8e..673f679740 100644 --- a/dart/test/sentry_envelope_item_test.dart +++ b/dart/test/sentry_envelope_item_test.dart @@ -8,7 +8,6 @@ import 'package:sentry/src/sentry_envelope_item_header.dart'; import 'package:sentry/src/sentry_item_type.dart'; import 'package:sentry/src/sentry_tracer.dart'; import 'package:sentry/src/transport/data_category.dart'; -import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks/mock_hub.dart'; diff --git a/dart/test/sentry_envelope_test.dart b/dart/test/sentry_envelope_test.dart index 063be42ba9..375f62846e 100644 --- a/dart/test/sentry_envelope_test.dart +++ b/dart/test/sentry_envelope_test.dart @@ -6,7 +6,6 @@ import 'package:sentry/src/sentry_envelope_header.dart'; import 'package:sentry/src/sentry_envelope_item_header.dart'; import 'package:sentry/src/sentry_item_type.dart'; import 'package:sentry/src/sentry_tracer.dart'; -import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks.dart'; diff --git a/dart/test/sentry_event_test.dart b/dart/test/sentry_event_test.dart index 58fb946110..4e661638a8 100644 --- a/dart/test/sentry_event_test.dart +++ b/dart/test/sentry_event_test.dart @@ -5,7 +5,6 @@ import 'package:collection/collection.dart'; import 'package:sentry/sentry.dart'; import 'package:sentry/src/version.dart'; -import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; void main() { diff --git a/dart/test/sentry_span_test.dart b/dart/test/sentry_span_test.dart index 37a81309da..e878be5cf2 100644 --- a/dart/test/sentry_span_test.dart +++ b/dart/test/sentry_span_test.dart @@ -1,6 +1,5 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_tracer.dart'; -import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks/mock_hub.dart'; diff --git a/dart/test/sentry_tracer_test.dart b/dart/test/sentry_tracer_test.dart index f2c0bcdf7c..cb9f72fdfc 100644 --- a/dart/test/sentry_tracer_test.dart +++ b/dart/test/sentry_tracer_test.dart @@ -1,6 +1,5 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/sentry_tracer.dart'; -import 'package:sentry/src/utils.dart'; import 'package:test/test.dart'; import 'mocks.dart'; diff --git a/dio/lib/src/dio_event_processor.dart b/dio/lib/src/dio_event_processor.dart index 0e9eedd5eb..3a9603abd1 100644 --- a/dio/lib/src/dio_event_processor.dart +++ b/dio/lib/src/dio_event_processor.dart @@ -1,5 +1,7 @@ // ignore_for_file: deprecated_member_use +import 'dart:convert'; + import 'package:dio/dio.dart'; import 'package:sentry/sentry.dart'; @@ -62,7 +64,7 @@ class DioEventProcessor implements EventProcessor { } /// Returns the request data, if possible according to the users settings. - Object? _getRequestData(dynamic data) { + Object? _getRequestData(Object? data) { if (!_options.sendDefaultPii) { return null; } @@ -87,8 +89,74 @@ class DioEventProcessor implements EventProcessor { return SentryResponse( headers: _options.sendDefaultPii ? headers : null, - bodySize: dioError.response?.data?.length as int?, + bodySize: _getBodySize( + dioError.response?.data, + dioError.requestOptions.responseType, + ), statusCode: response?.statusCode, + data: _getResponseData( + dioError.response?.data, + dioError.requestOptions.responseType, + ), ); } + + /// Returns the response data, if possible according to the users settings. + Object? _getResponseData(Object? data, ResponseType responseType) { + if (!_options.sendDefaultPii || data == null) { + return null; + } + switch (responseType) { + case ResponseType.json: + // ignore: invalid_use_of_internal_member + final jsData = utf8JsonEncoder.convert(data); + if (_options.maxResponseBodySize.shouldAddBody(jsData.length)) { + return data; + } + break; + case ResponseType.stream: + break; // No support for logging stream body. + case ResponseType.plain: + if (data is String && + _options.maxResponseBodySize.shouldAddBody(data.codeUnits.length)) { + return data; + } + break; + case ResponseType.bytes: + if (data is List && + _options.maxResponseBodySize.shouldAddBody(data.length)) { + return data; + } + break; + } + return null; + } + + int? _getBodySize(Object? data, ResponseType responseType) { + if (data == null) { + return null; + } + switch (responseType) { + case ResponseType.json: + return json.encode(data).codeUnits.length; + case ResponseType.stream: + if (data is String) { + return data.length; + } else { + return null; + } + case ResponseType.plain: + if (data is String) { + return data.codeUnits.length; + } else { + return null; + } + case ResponseType.bytes: + if (data is List) { + return data.length; + } else { + return null; + } + } + } } diff --git a/dio/lib/src/version.dart b/dio/lib/src/version.dart index 6e137694e7..0efaa7daaa 100644 --- a/dio/lib/src/version.dart +++ b/dio/lib/src/version.dart @@ -1,5 +1,5 @@ /// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '7.8.0'; +const String sdkVersion = '7.9.0'; /// The package name reported to Sentry.io in the submitted events. const String packageName = 'pub:sentry_dio'; diff --git a/dio/pubspec.yaml b/dio/pubspec.yaml index 2f46b81ed4..1343b6127b 100644 --- a/dio/pubspec.yaml +++ b/dio/pubspec.yaml @@ -1,6 +1,6 @@ name: sentry_dio description: An integration which adds support for performance tracing for the Dio package. -version: 7.8.0 +version: 7.9.0 homepage: https://docs.sentry.io/platforms/dart/ repository: https://github.com/getsentry/sentry-dart issue_tracker: https://github.com/getsentry/sentry-dart/issues @@ -11,7 +11,7 @@ environment: dependencies: dio: ^5.0.0 - sentry: 7.8.0 + sentry: 7.9.0 dev_dependencies: meta: ^1.3.0 diff --git a/dio/test/dio_event_processor_test.dart b/dio/test/dio_event_processor_test.dart index 62520b6f44..af45d0c681 100644 --- a/dio/test/dio_event_processor_test.dart +++ b/dio/test/dio_event_processor_test.dart @@ -132,6 +132,64 @@ void main() { expect(processedEvent.request?.headers, {}); }); + + test('request body is included according to $MaxResponseBodySize', + () async { + final scenarios = [ + // never + MaxBodySizeTestConfig(MaxRequestBodySize.never, 0, false), + MaxBodySizeTestConfig(MaxRequestBodySize.never, 4001, false), + MaxBodySizeTestConfig(MaxRequestBodySize.never, 10001, false), + // always + MaxBodySizeTestConfig(MaxRequestBodySize.always, 0, true), + MaxBodySizeTestConfig(MaxRequestBodySize.always, 4001, true), + MaxBodySizeTestConfig(MaxRequestBodySize.always, 10001, true), + // small + MaxBodySizeTestConfig(MaxRequestBodySize.small, 0, true), + MaxBodySizeTestConfig(MaxRequestBodySize.small, 4000, true), + MaxBodySizeTestConfig(MaxRequestBodySize.small, 4001, false), + // medium + MaxBodySizeTestConfig(MaxRequestBodySize.medium, 0, true), + MaxBodySizeTestConfig(MaxRequestBodySize.medium, 4001, true), + MaxBodySizeTestConfig(MaxRequestBodySize.medium, 10000, true), + MaxBodySizeTestConfig(MaxRequestBodySize.medium, 10001, false), + ]; + + for (final scenario in scenarios) { + final sut = fixture.getSut( + sendDefaultPii: true, + captureFailedRequests: true, + maxRequestBodySize: scenario.maxBodySize, + ); + + final data = List.generate(scenario.contentLength, (index) => 0); + final request = requestOptions.copyWith(method: 'POST', data: data); + final throwable = Exception(); + final dioError = DioError( + requestOptions: request, + response: Response( + requestOptions: request, + statusCode: 401, + data: data, + ), + ); + final event = SentryEvent( + throwable: throwable, + exceptions: [ + fixture.sentryError(throwable), + fixture.sentryError(dioError) + ], + ); + final processedEvent = sut.apply(event) as SentryEvent; + final capturedRequest = processedEvent.request; + + expect(capturedRequest, isNotNull); + expect( + capturedRequest?.data, + scenario.shouldBeIncluded ? isNotNull : isNull, + ); + } + }); }); group('response', () { @@ -140,6 +198,7 @@ void main() { final request = requestOptions.copyWith( method: 'POST', + responseType: ResponseType.plain, ); final throwable = Exception(); final dioError = DioError( @@ -181,6 +240,7 @@ void main() { final request = requestOptions.copyWith( method: 'POST', + responseType: ResponseType.plain, ); final throwable = Exception(); final dioError = DioError( @@ -211,6 +271,121 @@ void main() { expect(processedEvent.contexts.response?.statusCode, 200); expect(processedEvent.contexts.response?.headers, {}); }); + + test('response body is included according to $MaxResponseBodySize', + () async { + final scenarios = [ + // never + MaxBodySizeTestConfig(MaxResponseBodySize.never, 0, false), + MaxBodySizeTestConfig(MaxResponseBodySize.never, 4001, false), + MaxBodySizeTestConfig(MaxResponseBodySize.never, 10001, false), + // always + MaxBodySizeTestConfig(MaxResponseBodySize.always, 0, true), + MaxBodySizeTestConfig(MaxResponseBodySize.always, 4001, true), + MaxBodySizeTestConfig(MaxResponseBodySize.always, 10001, true), + // small + MaxBodySizeTestConfig(MaxResponseBodySize.small, 0, true), + MaxBodySizeTestConfig(MaxResponseBodySize.small, 4000, true), + MaxBodySizeTestConfig(MaxResponseBodySize.small, 4001, false), + // medium + MaxBodySizeTestConfig(MaxResponseBodySize.medium, 0, true), + MaxBodySizeTestConfig(MaxResponseBodySize.medium, 4001, true), + MaxBodySizeTestConfig(MaxResponseBodySize.medium, 10000, true), + MaxBodySizeTestConfig(MaxResponseBodySize.medium, 10001, false), + ]; + + for (final scenario in scenarios) { + final sut = fixture.getSut( + sendDefaultPii: true, + captureFailedRequests: true, + maxResponseBodySize: scenario.maxBodySize, + ); + + final data = List.generate(scenario.contentLength, (index) => 0); + final request = requestOptions.copyWith( + method: 'POST', + data: data, + responseType: ResponseType.bytes, + ); + final throwable = Exception(); + final dioError = DioError( + requestOptions: request, + response: Response( + requestOptions: request, + statusCode: 401, + data: data, + ), + ); + final event = SentryEvent( + throwable: throwable, + exceptions: [ + fixture.sentryError(throwable), + fixture.sentryError(dioError) + ], + ); + final processedEvent = sut.apply(event) as SentryEvent; + final capturedResponse = processedEvent.contexts.response; + + expect(capturedResponse, isNotNull); + expect( + capturedResponse?.data, + scenario.shouldBeIncluded ? isNotNull : isNull, + ); + } + }); + + test('data supports all response body types', () async { + final dataByType = { + ResponseType.plain: ['plain'], + ResponseType.bytes: [ + [1337] + ], + ResponseType.json: [ + 9001, + null, + 'string', + true, + ['list'], + {'map-key': 'map-value'}, + ] + }; + + for (final entry in dataByType.entries) { + final responseType = entry.key; + + for (final data in entry.value) { + final request = requestOptions.copyWith( + method: 'POST', + data: data, + responseType: responseType, + ); + final throwable = Exception(); + final dioError = DioError( + requestOptions: request, + response: Response( + requestOptions: request, + statusCode: 401, + data: data, + ), + ); + + final sut = fixture.getSut(sendDefaultPii: true); + + final event = SentryEvent( + throwable: throwable, + exceptions: [ + fixture.sentryError(throwable), + fixture.sentryError(dioError) + ], + ); + final processedEvent = sut.apply(event) as SentryEvent; + final capturedResponse = processedEvent.contexts.response; + + expect(capturedResponse, isNotNull); + expect(capturedResponse?.data, data); + } + } + }); }); test('$DioEventProcessor adds chained stacktraces', () { @@ -266,12 +441,18 @@ class Fixture { // ignore: invalid_use_of_internal_member SentryExceptionFactory get exceptionFactory => options.exceptionFactory; - DioEventProcessor getSut({bool sendDefaultPii = false}) { + DioEventProcessor getSut({ + bool sendDefaultPii = false, + bool captureFailedRequests = true, + MaxRequestBodySize maxRequestBodySize = MaxRequestBodySize.always, + MaxResponseBodySize maxResponseBodySize = MaxResponseBodySize.always, + }) { return DioEventProcessor( options ..sendDefaultPii = sendDefaultPii - ..maxRequestBodySize = MaxRequestBodySize.always - ..maxResponseBodySize = MaxResponseBodySize.always, + ..captureFailedRequests = captureFailedRequests + ..maxRequestBodySize = maxRequestBodySize + ..maxResponseBodySize = maxResponseBodySize, ); } @@ -283,3 +464,17 @@ class Fixture { ); } } + +class MaxBodySizeTestConfig { + MaxBodySizeTestConfig( + this.maxBodySize, + this.contentLength, + this.shouldBeIncluded, + ); + + final T maxBodySize; + final int contentLength; + final bool shouldBeIncluded; + + Matcher get matcher => shouldBeIncluded ? isNotNull : isNull; +} diff --git a/file/lib/src/version.dart b/file/lib/src/version.dart index 4f79cf0c55..5b59773228 100644 --- a/file/lib/src/version.dart +++ b/file/lib/src/version.dart @@ -1,5 +1,5 @@ /// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '7.8.0'; +const String sdkVersion = '7.9.0'; /// The package name reported to Sentry.io in the submitted events. const String packageName = 'pub:sentry_file'; diff --git a/file/pubspec.yaml b/file/pubspec.yaml index e585c11379..d6fcad6e27 100644 --- a/file/pubspec.yaml +++ b/file/pubspec.yaml @@ -1,6 +1,6 @@ name: sentry_file description: An integration which adds support for performance tracing for dart.io.File. -version: 7.8.0 +version: 7.9.0 homepage: https://docs.sentry.io/platforms/dart/ repository: https://github.com/getsentry/sentry-dart issue_tracker: https://github.com/getsentry/sentry-dart/issues @@ -10,7 +10,7 @@ environment: sdk: '>=2.19.0 <4.0.0' dependencies: - sentry: 7.8.0 + sentry: 7.9.0 meta: ^1.3.0 dev_dependencies: diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index 9d02d1ecba..6d657a589e 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:io' show Platform; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index 648b41dd44..7b523385d8 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -1,6 +1,6 @@ name: sentry_flutter_example description: Demonstrates how to use the sentry_flutter plugin. -version: 7.8.0 +version: 7.9.0 publish_to: 'none' # Remove this line if you wish to publish to pub.dev diff --git a/flutter/ios/Classes/SentryFlutterPluginApple.swift b/flutter/ios/Classes/SentryFlutterPluginApple.swift index 42f55d3f3b..e5ae1a6c13 100644 --- a/flutter/ios/Classes/SentryFlutterPluginApple.swift +++ b/flutter/ios/Classes/SentryFlutterPluginApple.swift @@ -384,6 +384,10 @@ public class SentryFlutterPluginApple: NSObject, FlutterPlugin { if let enableAppHangTracking = arguments["enableAppHangTracking"] as? Bool { options.enableAppHangTracking = enableAppHangTracking } + + if let appHangTimeoutIntervalMillis = arguments["appHangTimeoutIntervalMillis"] as? UInt { + options.appHangTimeoutInterval = TimeInterval(appHangTimeoutIntervalMillis) / 1000 + } } private func logLevelFrom(diagnosticLevel: String) -> SentryLevel { diff --git a/flutter/lib/src/integrations/flutter_error_integration.dart b/flutter/lib/src/integrations/flutter_error_integration.dart index a9f10bcc09..03f8436bf9 100644 --- a/flutter/lib/src/integrations/flutter_error_integration.dart +++ b/flutter/lib/src/integrations/flutter_error_integration.dart @@ -54,11 +54,8 @@ class FlutterErrorIntegration implements Integration { stackTrace: errorDetails.stack, ); - // FlutterError doesn't crash the App. - final mechanism = Mechanism( - type: 'FlutterError', - handled: true, - ); + // FlutterError doesn't crash the app, but is not handled by the user. + final mechanism = Mechanism(type: 'FlutterError', handled: false); final throwableMechanism = ThrowableMechanism(mechanism, exception); var event = SentryEvent( diff --git a/flutter/lib/src/integrations/native_sdk_integration.dart b/flutter/lib/src/integrations/native_sdk_integration.dart index 89f0c5ada2..9c4a98379e 100644 --- a/flutter/lib/src/integrations/native_sdk_integration.dart +++ b/flutter/lib/src/integrations/native_sdk_integration.dart @@ -51,6 +51,8 @@ class NativeSdkIntegration implements Integration { 'enableAppHangTracking': options.enableAppHangTracking, 'connectionTimeoutMillis': options.connectionTimeout.inMilliseconds, 'readTimeoutMillis': options.readTimeout.inMilliseconds, + 'appHangTimeoutIntervalMillis': + options.appHangTimeoutInterval.inMilliseconds, }); options.sdk.addIntegration('nativeSdkIntegration'); diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index 2f5ccd96d1..a1d7755c3c 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -195,10 +195,18 @@ class SentryFlutterOptions extends SentryOptions { bool attachViewHierarchy = false; /// When enabled, the SDK tracks when the application stops responding for a - /// specific amount of time (default 2s). + /// specific amount of time, See [appHangTimeoutInterval]. /// Only available on iOS and macOS. bool enableAppHangTracking = true; + /// The minimum amount of time an app should be unresponsive to be classified + /// as an App Hanging. The actual amount may be a little longer. Avoid using + /// values lower than 100ms, which may cause a lot of app hangs events being + /// transmitted. + /// Default to 2s. + /// Only available on iOS and macOS. + Duration appHangTimeoutInterval = Duration(seconds: 2); + /// Connection timeout. This will only be synced to the Android native SDK. Duration connectionTimeout = Duration(seconds: 5); diff --git a/flutter/lib/src/version.dart b/flutter/lib/src/version.dart index 97bb83ee2e..0da840e5b1 100644 --- a/flutter/lib/src/version.dart +++ b/flutter/lib/src/version.dart @@ -1,5 +1,5 @@ /// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '7.8.0'; +const String sdkVersion = '7.9.0'; /// The default SDK name reported to Sentry.io in the submitted events. const String sdkName = 'sentry.dart.flutter'; diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 110c60a8a3..f49fb9ca53 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -1,5 +1,5 @@ name: sentry_flutter -version: 7.8.0 +version: 7.9.0 description: Sentry SDK for Flutter. This package aims to support different Flutter targets by relying on the many platforms supported by Sentry with native SDKs. homepage: https://docs.sentry.io/platforms/flutter/ repository: https://github.com/getsentry/sentry-dart @@ -15,7 +15,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - sentry: 7.8.0 + sentry: 7.9.0 package_info_plus: '>=1.0.0 <5.0.0' meta: ^1.3.0 diff --git a/flutter/test/integrations/flutter_error_integration_test.dart b/flutter/test/integrations/flutter_error_integration_test.dart index 53446caeda..43df8de278 100644 --- a/flutter/test/integrations/flutter_error_integration_test.dart +++ b/flutter/test/integrations/flutter_error_integration_test.dart @@ -70,7 +70,7 @@ void main() { final throwableMechanism = event.throwableMechanism as ThrowableMechanism; expect(throwableMechanism.mechanism.type, 'FlutterError'); - expect(throwableMechanism.mechanism.handled, true); + expect(throwableMechanism.mechanism.handled, false); expect(throwableMechanism.throwable, exception); expect(event.contexts['flutter_error_details']['library'], 'sentry'); @@ -102,7 +102,7 @@ void main() { final throwableMechanism = event.throwableMechanism as ThrowableMechanism; expect(throwableMechanism.mechanism.type, 'FlutterError'); - expect(throwableMechanism.mechanism.handled, true); + expect(throwableMechanism.mechanism.handled, false); expect(event.contexts['flutter_error_details']['library'], 'sentry'); expect(event.contexts['flutter_error_details']['context'], @@ -126,7 +126,7 @@ void main() { final throwableMechanism = event.throwableMechanism as ThrowableMechanism; expect(throwableMechanism.mechanism.type, 'FlutterError'); - expect(throwableMechanism.mechanism.handled, true); + expect(throwableMechanism.mechanism.handled, false); expect(throwableMechanism.mechanism.data['hint'], isNull); expect(event.contexts['flutter_error_details'], isNull); diff --git a/flutter/test/integrations/init_native_sdk_integration_test.dart b/flutter/test/integrations/init_native_sdk_integration_test.dart index ec1e59f94f..4ed6a3d3b9 100644 --- a/flutter/test/integrations/init_native_sdk_integration_test.dart +++ b/flutter/test/integrations/init_native_sdk_integration_test.dart @@ -62,6 +62,7 @@ void main() { 'enableAppHangTracking': true, 'connectionTimeoutMillis': 5000, 'readTimeoutMillis': 5000, + 'appHangTimeoutIntervalMillis': 2000, }); }); @@ -100,7 +101,8 @@ void main() { ..captureFailedRequests = false ..enableAppHangTracking = false ..connectionTimeout = Duration(milliseconds: 9001) - ..readTimeout = Duration(milliseconds: 9002); + ..readTimeout = Duration(milliseconds: 9002) + ..appHangTimeoutInterval = Duration(milliseconds: 9003); options.sdk.addIntegration('foo'); options.sdk.addPackage('bar', '1'); @@ -143,6 +145,7 @@ void main() { 'enableAppHangTracking': false, 'connectionTimeoutMillis': 9001, 'readTimeoutMillis': 9002, + 'appHangTimeoutIntervalMillis': 9003, }); }); diff --git a/logging/lib/src/version.dart b/logging/lib/src/version.dart index f507cad902..3905a5c4dc 100644 --- a/logging/lib/src/version.dart +++ b/logging/lib/src/version.dart @@ -1,5 +1,5 @@ /// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '7.8.0'; +const String sdkVersion = '7.9.0'; /// The package name reported to Sentry.io in the submitted events. const String packageName = 'pub:sentry_logging'; diff --git a/logging/pubspec.yaml b/logging/pubspec.yaml index c8150b1dce..b964b9d9fd 100644 --- a/logging/pubspec.yaml +++ b/logging/pubspec.yaml @@ -1,6 +1,6 @@ name: sentry_logging description: An integration which adds support for recording log from the logging package. -version: 7.8.0 +version: 7.9.0 homepage: https://docs.sentry.io/platforms/dart/ repository: https://github.com/getsentry/sentry-dart issue_tracker: https://github.com/getsentry/sentry-dart/issues @@ -11,7 +11,7 @@ environment: dependencies: logging: ^1.0.0 - sentry: 7.8.0 + sentry: 7.9.0 dev_dependencies: lints: ^2.0.0 diff --git a/sqflite/lib/src/version.dart b/sqflite/lib/src/version.dart index 328e77c6bd..67be6e3068 100644 --- a/sqflite/lib/src/version.dart +++ b/sqflite/lib/src/version.dart @@ -1,5 +1,5 @@ /// The SDK version reported to Sentry.io in the submitted events. -const String sdkVersion = '7.8.0'; +const String sdkVersion = '7.9.0'; /// The package name reported to Sentry.io in the submitted events. const String packageName = 'pub:sentry_sqflite'; diff --git a/sqflite/pubspec.yaml b/sqflite/pubspec.yaml index a04ca6344f..db60eec2a5 100644 --- a/sqflite/pubspec.yaml +++ b/sqflite/pubspec.yaml @@ -1,6 +1,6 @@ name: sentry_sqflite description: An integration which adds support for performance tracing for the sqflite package. -version: 7.8.0 +version: 7.9.0 homepage: https://docs.sentry.io/platforms/flutter/ repository: https://github.com/getsentry/sentry-dart issue_tracker: https://github.com/getsentry/sentry-dart/issues @@ -10,7 +10,7 @@ environment: flutter: '>=3.3.0' # matching sqflite dependencies: - sentry: 7.8.0 + sentry: 7.9.0 sqflite: ^2.0.0 sqflite_common: ^2.0.0 meta: ^1.3.0