diff --git a/dart/lib/sentry.dart b/dart/lib/sentry.dart index 5d8304375d..374265d077 100644 --- a/dart/lib/sentry.dart +++ b/dart/lib/sentry.dart @@ -10,4 +10,5 @@ export 'src/sentry_client.dart'; export 'src/hub.dart'; export 'src/sentry_options.dart'; export 'src/transport/transport.dart'; +// useful for integrations export 'src/throwable_mechanism.dart'; diff --git a/dart/lib/src/protocol/noop_origin.dart b/dart/lib/src/noop_origin.dart similarity index 100% rename from dart/lib/src/protocol/noop_origin.dart rename to dart/lib/src/noop_origin.dart diff --git a/dart/lib/src/protocol/origin.dart b/dart/lib/src/origin.dart similarity index 100% rename from dart/lib/src/protocol/origin.dart rename to dart/lib/src/origin.dart diff --git a/dart/lib/src/sentry_stack_trace_factory.dart b/dart/lib/src/sentry_stack_trace_factory.dart index 957de1eccf..a64aa8d355 100644 --- a/dart/lib/src/sentry_stack_trace_factory.dart +++ b/dart/lib/src/sentry_stack_trace_factory.dart @@ -1,8 +1,7 @@ import 'package:meta/meta.dart'; import 'package:stack_trace/stack_trace.dart'; -import 'protocol/noop_origin.dart' - if (dart.library.html) 'protocol/origin.dart'; +import 'noop_origin.dart' if (dart.library.html) 'origin.dart'; import 'protocol.dart'; import 'sentry_options.dart'; diff --git a/dart/test/stack_trace_test.dart b/dart/test/stack_trace_test.dart index 4bb45e9496..4445b73c57 100644 --- a/dart/test/stack_trace_test.dart +++ b/dart/test/stack_trace_test.dart @@ -3,8 +3,8 @@ // found in the LICENSE file. import 'package:sentry/sentry.dart'; -import 'package:sentry/src/protocol/noop_origin.dart' - if (dart.library.html) 'package:sentry/src/protocol/origin.dart'; +import 'package:sentry/src/noop_origin.dart' + if (dart.library.html) 'package:sentry/src/origin.dart'; import 'package:sentry/src/sentry_stack_trace_factory.dart'; import 'package:stack_trace/stack_trace.dart'; import 'package:test/test.dart'; diff --git a/flutter/README.md b/flutter/README.md index 3f92ab2392..7645624f41 100644 --- a/flutter/README.md +++ b/flutter/README.md @@ -32,7 +32,6 @@ The current stable version is the Dart SDK, [3.0.1](https://pub.dev/packages/sen ```dart import 'package:flutter/widgets.dart'; -import 'package:sentry/sentry.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; Future main() async { diff --git a/flutter/example/android/gradle.properties b/flutter/example/android/gradle.properties index d9cf55df7c..46ec45ad33 100644 --- a/flutter/example/android/gradle.properties +++ b/flutter/example/android/gradle.properties @@ -1,2 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true +android.enableR8=true diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart index e7e552fa2e..654e526115 100644 --- a/flutter/example/lib/main.dart +++ b/flutter/example/lib/main.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:sentry/sentry.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:universal_platform/universal_platform.dart'; @@ -45,6 +44,7 @@ class _MyAppState extends State { appBar: AppBar(title: const Text('Sentry Flutter Example')), body: Column( children: [ + const Center(child: Text('Trigger an action:\n')), RaisedButton( child: const Text('Dart: try catch'), onPressed: () => tryCatch(), diff --git a/flutter/lib/sentry_flutter.dart b/flutter/lib/sentry_flutter.dart index cdb891bf6e..ba6831738d 100644 --- a/flutter/lib/sentry_flutter.dart +++ b/flutter/lib/sentry_flutter.dart @@ -1,151 +1,5 @@ -import 'dart:async'; -import 'dart:io'; +/// A Flutter client for Sentry.io crash reporting. +export 'package:sentry/sentry.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:package_info/package_info.dart'; -import 'package:sentry/sentry.dart'; - -import 'default_integrations.dart'; -import 'file_system_transport.dart'; -import 'version.dart'; - -mixin SentryFlutter { - static const _channel = MethodChannel('sentry_flutter'); - - static Future init( - OptionsConfiguration optionsConfiguration, - Function callback, { - PackageLoader packageLoader = _loadPackageInfo, - }) async { - await Sentry.init((options) async { - await _initDefaultValues(options, callback, packageLoader); - - await optionsConfiguration(options); - }); - } - - static Future _initDefaultValues( - SentryOptions options, - Function callback, - PackageLoader packageLoader, - ) async { - // it is necessary to initialize Flutter method channels so that - // our plugin can call into the native code. - WidgetsFlutterBinding.ensureInitialized(); - - options.debug = kDebugMode; - - // web still uses a http transport for Web which is set by default - if (!kIsWeb) { - options.transport = FileSystemTransport(_channel, options); - } - - // if no environment is set, we set 'production' by default, but if we know it's - // a non-release build, or the SENTRY_ENVIRONMENT is set, we read from it. - if (const bool.hasEnvironment('SENTRY_ENVIRONMENT') || !kReleaseMode) { - options.environment = const String.fromEnvironment('SENTRY_ENVIRONMENT', - defaultValue: 'debug'); - } - - // if the SENTRY_DSN is set, we read from it. - options.dsn = const bool.hasEnvironment('SENTRY_DSN') - ? const String.fromEnvironment('SENTRY_DSN') - : options.dsn; - - // TODO: load debug images when split symbols are enabled. - - // first step is to install the native integration and set default values, - // so we are able to capture future errors. - _addDefaultIntegrations(options, callback); - - await _setReleaseAndDist(options, packageLoader); - - _setSdk(options); - } - - static Future _setReleaseAndDist( - SentryOptions options, - PackageLoader packageLoader, - ) async { - try { - if (!kIsWeb) { - if (packageLoader == null) { - options.logger(SentryLevel.debug, 'Package loader is null.'); - return; - } - final packageInfo = await packageLoader(); - final release = - '${packageInfo.packageName}@${packageInfo.version}+${packageInfo.buildNumber}'; - options.logger(SentryLevel.debug, 'release: $release'); - - options.release = release; - options.dist = packageInfo.buildNumber; - } else { - // for non-mobile builds, we read the release and dist from the - // system variables (SENTRY_RELEASE and SENTRY_DIST). - options.release = const bool.hasEnvironment('SENTRY_RELEASE') - ? const String.fromEnvironment('SENTRY_RELEASE') - : options.release; - options.dist = const bool.hasEnvironment('SENTRY_DIST') - ? const String.fromEnvironment('SENTRY_DIST') - : options.dist; - } - } catch (error) { - options.logger( - SentryLevel.error, 'Failed to load release and dist: $error'); - } - } - - /// Install default integrations - /// https://medium.com/flutter-community/error-handling-in-flutter-98fce88a34f0 - static void _addDefaultIntegrations( - SentryOptions options, - Function callback, - ) { - // the ordering here matters, as we'd like to first start the native integration - // that allow us to send events to the network and then the Flutter integrations. - // Flutter Web doesn't need that, only Android and iOS. - if (!kIsWeb) { - options.addIntegration(nativeSdkIntegration(options, _channel)); - } - - // will catch any errors that may occur in the Flutter framework itself. - options.addIntegration(flutterErrorIntegration); - - // Throws when running on the browser - if (!kIsWeb) { - // catch any errors that may occur within the entry function, main() - // in the ‘root zone’ where all Dart programs start - options.addIntegration(isolateErrorIntegration); - } - - // TODO: make it testable/mockable - if (Platform.isIOS) { - options.addIntegration(loadContextsIntegration(options, _channel)); - } - // finally the runZonedGuarded, catch any errors in Dart code running - // ‘outside’ the Flutter framework - options.addIntegration(runZonedGuardedIntegration(callback)); - } - - static void _setSdk(SentryOptions options) { - // overwrite sdk info with current flutter sdk - final sdk = SdkVersion( - name: sdkName, - version: sdkVersion, - integrations: List.from(options.sdk.integrations), - packages: List.from(options.sdk.packages), - ); - sdk.addPackage('pub:sentry_flutter', sdkVersion); - options.sdk = sdk; - } -} - -typedef PackageLoader = Future Function(); - -/// Package info loader. -Future _loadPackageInfo() async { - return await PackageInfo.fromPlatform(); -} +export 'src/default_integrations.dart'; +export 'src/sentry_flutter.dart'; diff --git a/flutter/lib/default_integrations.dart b/flutter/lib/src/default_integrations.dart similarity index 100% rename from flutter/lib/default_integrations.dart rename to flutter/lib/src/default_integrations.dart diff --git a/flutter/lib/file_system_transport.dart b/flutter/lib/src/file_system_transport.dart similarity index 100% rename from flutter/lib/file_system_transport.dart rename to flutter/lib/src/file_system_transport.dart diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart new file mode 100644 index 0000000000..cdb891bf6e --- /dev/null +++ b/flutter/lib/src/sentry_flutter.dart @@ -0,0 +1,151 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:package_info/package_info.dart'; +import 'package:sentry/sentry.dart'; + +import 'default_integrations.dart'; +import 'file_system_transport.dart'; +import 'version.dart'; + +mixin SentryFlutter { + static const _channel = MethodChannel('sentry_flutter'); + + static Future init( + OptionsConfiguration optionsConfiguration, + Function callback, { + PackageLoader packageLoader = _loadPackageInfo, + }) async { + await Sentry.init((options) async { + await _initDefaultValues(options, callback, packageLoader); + + await optionsConfiguration(options); + }); + } + + static Future _initDefaultValues( + SentryOptions options, + Function callback, + PackageLoader packageLoader, + ) async { + // it is necessary to initialize Flutter method channels so that + // our plugin can call into the native code. + WidgetsFlutterBinding.ensureInitialized(); + + options.debug = kDebugMode; + + // web still uses a http transport for Web which is set by default + if (!kIsWeb) { + options.transport = FileSystemTransport(_channel, options); + } + + // if no environment is set, we set 'production' by default, but if we know it's + // a non-release build, or the SENTRY_ENVIRONMENT is set, we read from it. + if (const bool.hasEnvironment('SENTRY_ENVIRONMENT') || !kReleaseMode) { + options.environment = const String.fromEnvironment('SENTRY_ENVIRONMENT', + defaultValue: 'debug'); + } + + // if the SENTRY_DSN is set, we read from it. + options.dsn = const bool.hasEnvironment('SENTRY_DSN') + ? const String.fromEnvironment('SENTRY_DSN') + : options.dsn; + + // TODO: load debug images when split symbols are enabled. + + // first step is to install the native integration and set default values, + // so we are able to capture future errors. + _addDefaultIntegrations(options, callback); + + await _setReleaseAndDist(options, packageLoader); + + _setSdk(options); + } + + static Future _setReleaseAndDist( + SentryOptions options, + PackageLoader packageLoader, + ) async { + try { + if (!kIsWeb) { + if (packageLoader == null) { + options.logger(SentryLevel.debug, 'Package loader is null.'); + return; + } + final packageInfo = await packageLoader(); + final release = + '${packageInfo.packageName}@${packageInfo.version}+${packageInfo.buildNumber}'; + options.logger(SentryLevel.debug, 'release: $release'); + + options.release = release; + options.dist = packageInfo.buildNumber; + } else { + // for non-mobile builds, we read the release and dist from the + // system variables (SENTRY_RELEASE and SENTRY_DIST). + options.release = const bool.hasEnvironment('SENTRY_RELEASE') + ? const String.fromEnvironment('SENTRY_RELEASE') + : options.release; + options.dist = const bool.hasEnvironment('SENTRY_DIST') + ? const String.fromEnvironment('SENTRY_DIST') + : options.dist; + } + } catch (error) { + options.logger( + SentryLevel.error, 'Failed to load release and dist: $error'); + } + } + + /// Install default integrations + /// https://medium.com/flutter-community/error-handling-in-flutter-98fce88a34f0 + static void _addDefaultIntegrations( + SentryOptions options, + Function callback, + ) { + // the ordering here matters, as we'd like to first start the native integration + // that allow us to send events to the network and then the Flutter integrations. + // Flutter Web doesn't need that, only Android and iOS. + if (!kIsWeb) { + options.addIntegration(nativeSdkIntegration(options, _channel)); + } + + // will catch any errors that may occur in the Flutter framework itself. + options.addIntegration(flutterErrorIntegration); + + // Throws when running on the browser + if (!kIsWeb) { + // catch any errors that may occur within the entry function, main() + // in the ‘root zone’ where all Dart programs start + options.addIntegration(isolateErrorIntegration); + } + + // TODO: make it testable/mockable + if (Platform.isIOS) { + options.addIntegration(loadContextsIntegration(options, _channel)); + } + // finally the runZonedGuarded, catch any errors in Dart code running + // ‘outside’ the Flutter framework + options.addIntegration(runZonedGuardedIntegration(callback)); + } + + static void _setSdk(SentryOptions options) { + // overwrite sdk info with current flutter sdk + final sdk = SdkVersion( + name: sdkName, + version: sdkVersion, + integrations: List.from(options.sdk.integrations), + packages: List.from(options.sdk.packages), + ); + sdk.addPackage('pub:sentry_flutter', sdkVersion); + options.sdk = sdk; + } +} + +typedef PackageLoader = Future Function(); + +/// Package info loader. +Future _loadPackageInfo() async { + return await PackageInfo.fromPlatform(); +} diff --git a/flutter/lib/version.dart b/flutter/lib/src/version.dart similarity index 100% rename from flutter/lib/version.dart rename to flutter/lib/src/version.dart diff --git a/flutter/test/default_integrations_test.dart b/flutter/test/default_integrations_test.dart index d62035441f..fd5c841b38 100644 --- a/flutter/test/default_integrations_test.dart +++ b/flutter/test/default_integrations_test.dart @@ -3,7 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry_flutter/default_integrations.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'mocks.dart'; diff --git a/flutter/test/file_system_transport_test.dart b/flutter/test/file_system_transport_test.dart index 7180d2d73a..0892c4493d 100644 --- a/flutter/test/file_system_transport_test.dart +++ b/flutter/test/file_system_transport_test.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry_flutter/file_system_transport.dart'; +import 'package:sentry_flutter/src/file_system_transport.dart'; void main() { const MethodChannel _channel = MethodChannel('sentry_flutter'); diff --git a/flutter/test/sentry_flutter_util.dart b/flutter/test/sentry_flutter_util.dart index 61a684bf54..d9f9fe3c28 100644 --- a/flutter/test/sentry_flutter_util.dart +++ b/flutter/test/sentry_flutter_util.dart @@ -3,10 +3,9 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sentry/sentry.dart'; -import 'package:sentry_flutter/default_integrations.dart'; -import 'package:sentry_flutter/file_system_transport.dart'; -import 'package:sentry_flutter/version.dart'; - +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:sentry_flutter/src/file_system_transport.dart'; +import 'package:sentry_flutter/src/version.dart'; import 'mocks.dart'; FutureOr configurationTester(