From 849ae73868632e17ff795d10e9a76f979828f5ff Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Tue, 23 Nov 2021 11:00:26 +0100 Subject: [PATCH 1/6] Log errors captured in integrations with SentryLogger * add logger name parameter * log errors from Zone, Isolate and Flutter integrations --- dart/lib/src/default_integrations.dart | 8 ++++++++ dart/lib/src/diagnostic_logger.dart | 9 ++++++++- dart/lib/src/isolate_error_integration.dart | 8 ++++++++ dart/lib/src/sentry_options.dart | 9 ++++++--- flutter/lib/src/default_integrations.dart | 11 ++++++++++- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/dart/lib/src/default_integrations.dart b/dart/lib/src/default_integrations.dart index 3138d2703d..460aeeef41 100644 --- a/dart/lib/src/default_integrations.dart +++ b/dart/lib/src/default_integrations.dart @@ -27,6 +27,14 @@ class RunZonedGuardedIntegration extends Integration { await _runner(); }, (exception, stackTrace) async { + options.logger( + SentryLevel.error, + 'Uncaught zone error', + logger: 'sentry.runZonedGuarded', + exception: exception, + stackTrace: stackTrace, + ); + // runZonedGuarded doesn't crash the App. final mechanism = Mechanism(type: 'runZonedGuarded', handled: true); final throwableMechanism = ThrowableMechanism(mechanism, exception); diff --git a/dart/lib/src/diagnostic_logger.dart b/dart/lib/src/diagnostic_logger.dart index 82b6705ede..9a393f974f 100644 --- a/dart/lib/src/diagnostic_logger.dart +++ b/dart/lib/src/diagnostic_logger.dart @@ -10,11 +10,18 @@ class DiagnosticLogger { void log( SentryLevel level, String message, { + String? logger, Object? exception, StackTrace? stackTrace, }) { if (_isEnabled(level)) { - _logger(level, message, exception: exception, stackTrace: stackTrace); + _logger( + level, + message, + logger: logger, + exception: exception, + stackTrace: stackTrace, + ); } } diff --git a/dart/lib/src/isolate_error_integration.dart b/dart/lib/src/isolate_error_integration.dart index 95a39e0442..ed2109b772 100644 --- a/dart/lib/src/isolate_error_integration.dart +++ b/dart/lib/src/isolate_error_integration.dart @@ -52,6 +52,14 @@ Future handleIsolateError( final dynamic throwable = error.first; final dynamic stackTrace = error.last; + options.logger( + SentryLevel.error, + 'Uncaught isolate error', + logger: 'sentry.isolateError', + exception: throwable, + stackTrace: StackTrace.fromString(stackTrace), + ); + // Isolate errors don't crash the App. final mechanism = Mechanism(type: 'isolateError', handled: true); final throwableMechanism = ThrowableMechanism(mechanism, throwable); diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 7ef88bf336..64adfaeaa4 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -6,16 +6,16 @@ import 'package:http/http.dart'; import 'diagnostic_logger.dart'; import 'environment/environment_variables.dart'; import 'event_processor.dart'; +import 'http_client/sentry_http_client.dart'; import 'integration.dart'; import 'noop_client.dart'; +import 'platform_checker.dart'; import 'protocol.dart'; import 'tracing.dart'; import 'transport/noop_transport.dart'; import 'transport/transport.dart'; import 'utils.dart'; import 'version.dart'; -import 'platform_checker.dart'; -import 'http_client/sentry_http_client.dart'; // TODO: Scope observers, enableScopeSync // TODO: shutdownTimeout, flushTimeoutMillis @@ -320,6 +320,7 @@ typedef ClockProvider = DateTime Function(); typedef SentryLogger = void Function( SentryLevel level, String message, { + String? logger, Object? exception, StackTrace? stackTrace, }); @@ -331,6 +332,7 @@ typedef TracesSamplerCallback = double? Function( void noOpLogger( SentryLevel level, String message, { + String? logger, Object? exception, StackTrace? stackTrace, }) {} @@ -339,13 +341,14 @@ void noOpLogger( void dartLogger( SentryLevel level, String message, { + String? logger, Object? exception, StackTrace? stackTrace, }) { log( '[${level.name}] $message', level: level.toDartLogLevel(), - name: 'sentry', + name: logger ?? 'sentry', time: getUtcDateTime(), error: exception, stackTrace: stackTrace, diff --git a/flutter/lib/src/default_integrations.dart b/flutter/lib/src/default_integrations.dart index 13e6c3c1af..2404bd2501 100644 --- a/flutter/lib/src/default_integrations.dart +++ b/flutter/lib/src/default_integrations.dart @@ -1,10 +1,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:sentry/sentry.dart'; + import 'sentry_flutter_options.dart'; import 'widgets_binding_observer.dart'; @@ -70,6 +71,14 @@ class FlutterErrorIntegration extends Integration { if (library != null) 'library': library, }; + options.logger( + SentryLevel.error, + errorDetails.toStringShort(), + logger: 'sentry.flutterError', + exception: exception, + stackTrace: errorDetails.stack, + ); + // FlutterError doesn't crash the App. final mechanism = Mechanism( type: 'FlutterError', From 83b4e7d50ec66cd80ae17992c8fa3a11b4382a72 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Tue, 23 Nov 2021 12:48:52 +0100 Subject: [PATCH 2/6] Improve typing of isolate error handler and check for null stacktrace --- dart/lib/src/isolate_error_integration.dart | 12 ++++++------ dart/test/default_integrations_test.dart | 19 ++++++------------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/dart/lib/src/isolate_error_integration.dart b/dart/lib/src/isolate_error_integration.dart index ed2109b772..10d32f7d48 100644 --- a/dart/lib/src/isolate_error_integration.dart +++ b/dart/lib/src/isolate_error_integration.dart @@ -31,7 +31,7 @@ class IsolateErrorIntegration extends Integration { RawReceivePort _createPort(Hub hub, SentryOptions options) { return RawReceivePort( - (dynamic error) async { + (Object? error) async { await handleIsolateError(hub, options, error); }, ); @@ -42,22 +42,22 @@ RawReceivePort _createPort(Hub hub, SentryOptions options) { Future handleIsolateError( Hub hub, SentryOptions options, - dynamic error, + Object? error, ) async { options.logger(SentryLevel.debug, 'Capture from IsolateError $error'); // https://api.dartlang.org/stable/2.7.0/dart-isolate/Isolate/addErrorListener.html // error is a list of 2 elements - if (error is List && error.length == 2) { - final dynamic throwable = error.first; - final dynamic stackTrace = error.last; + if (error is List && error.length == 2) { + final Object? throwable = error.first; + final String? stackTrace = error.last; options.logger( SentryLevel.error, 'Uncaught isolate error', logger: 'sentry.isolateError', exception: throwable, - stackTrace: StackTrace.fromString(stackTrace), + stackTrace: stackTrace == null ? null : StackTrace.fromString(stackTrace), ); // Isolate errors don't crash the App. diff --git a/dart/test/default_integrations_test.dart b/dart/test/default_integrations_test.dart index 3c9e865503..e303af578e 100644 --- a/dart/test/default_integrations_test.dart +++ b/dart/test/default_integrations_test.dart @@ -2,8 +2,8 @@ import 'package:sentry/sentry.dart'; import 'package:sentry/src/noop_sentry_span.dart'; import 'package:test/test.dart'; -import 'mocks.dart'; import 'mocks/mock_hub.dart'; +import 'mocks.dart'; void main() { late Fixture fixture; @@ -35,7 +35,7 @@ void main() { 'Isolate error capture errors', () async { final throwable = StateError('error'); - final stackTrace = StackTrace.current; + final stackTrace = StackTrace.current.toString(); final error = [throwable, stackTrace]; // we could not find a way to trigger an error to the current Isolate @@ -66,10 +66,7 @@ void main() { await integration(fixture.hub, fixture.options); - expect( - true, - fixture.options.sdk.integrations - .contains('runZonedGuardedIntegration')); + expect(true, fixture.options.sdk.integrations.contains('runZonedGuardedIntegration')); }, onPlatform: { 'browser': Skip(), @@ -122,9 +119,7 @@ void main() { expect(breadcrumb.type, 'debug'); }); - test( - 'Run zoned guarded does not log calls to print as breadcrumb if disabled', - () async { + test('Run zoned guarded does not log calls to print as breadcrumb if disabled', () async { fixture.options.enablePrintBreadcrumbs = false; final integration = fixture.getSut(); @@ -185,10 +180,8 @@ class PrintRecursionMockHub extends MockHub { } @override - ISentrySpan startTransactionWithContext( - SentryTransactionContext transactionContext, - {Map? customSamplingContext, - bool? bindToScope}) { + ISentrySpan startTransactionWithContext(SentryTransactionContext transactionContext, + {Map? customSamplingContext, bool? bindToScope}) { return NoOpSentrySpan(); } } From 20e5b071a151cce7506e73e6932dafca7b146ca6 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Wed, 24 Nov 2021 14:01:19 +0100 Subject: [PATCH 3/6] Revert dynamic changes but correctly type the isolate error --- dart/lib/src/isolate_error_integration.dart | 12 +++++++++--- dart/test/default_integrations_test.dart | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dart/lib/src/isolate_error_integration.dart b/dart/lib/src/isolate_error_integration.dart index 10d32f7d48..600026e4ff 100644 --- a/dart/lib/src/isolate_error_integration.dart +++ b/dart/lib/src/isolate_error_integration.dart @@ -31,7 +31,7 @@ class IsolateErrorIntegration extends Integration { RawReceivePort _createPort(Hub hub, SentryOptions options) { return RawReceivePort( - (Object? error) async { + (dynamic error) async { await handleIsolateError(hub, options, error); }, ); @@ -42,14 +42,20 @@ RawReceivePort _createPort(Hub hub, SentryOptions options) { Future handleIsolateError( Hub hub, SentryOptions options, - Object? error, + dynamic error, ) async { options.logger(SentryLevel.debug, 'Capture from IsolateError $error'); // https://api.dartlang.org/stable/2.7.0/dart-isolate/Isolate/addErrorListener.html // error is a list of 2 elements if (error is List && error.length == 2) { - final Object? throwable = error.first; + /// The errors are sent back as two-element lists. + /// The first element is a `String` representation of the error, usually + /// created by calling `toString` on the error. + /// The second element is a `String` representation of an accompanying + /// stack trace, or `null` if no stack trace was provided. + /// To convert this back to a [StackTrace] object, use [StackTrace.fromString]. + final String throwable = error.first; final String? stackTrace = error.last; options.logger( diff --git a/dart/test/default_integrations_test.dart b/dart/test/default_integrations_test.dart index e303af578e..c40f633a37 100644 --- a/dart/test/default_integrations_test.dart +++ b/dart/test/default_integrations_test.dart @@ -34,7 +34,7 @@ void main() { test( 'Isolate error capture errors', () async { - final throwable = StateError('error'); + final throwable = StateError('error').toString(); final stackTrace = StackTrace.current.toString(); final error = [throwable, stackTrace]; From da4fdf111e6f1776c8216afb7410b44731059135 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Wed, 24 Nov 2021 14:37:53 +0100 Subject: [PATCH 4/6] Fix format --- dart/test/default_integrations_test.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dart/test/default_integrations_test.dart b/dart/test/default_integrations_test.dart index c40f633a37..6ca1bb64ec 100644 --- a/dart/test/default_integrations_test.dart +++ b/dart/test/default_integrations_test.dart @@ -66,7 +66,10 @@ void main() { await integration(fixture.hub, fixture.options); - expect(true, fixture.options.sdk.integrations.contains('runZonedGuardedIntegration')); + expect( + true, + fixture.options.sdk.integrations + .contains('runZonedGuardedIntegration')); }, onPlatform: { 'browser': Skip(), @@ -119,7 +122,9 @@ void main() { expect(breadcrumb.type, 'debug'); }); - test('Run zoned guarded does not log calls to print as breadcrumb if disabled', () async { + test( + 'Run zoned guarded does not log calls to print as breadcrumb if disabled', + () async { fixture.options.enablePrintBreadcrumbs = false; final integration = fixture.getSut(); @@ -180,8 +185,10 @@ class PrintRecursionMockHub extends MockHub { } @override - ISentrySpan startTransactionWithContext(SentryTransactionContext transactionContext, - {Map? customSamplingContext, bool? bindToScope}) { + ISentrySpan startTransactionWithContext( + SentryTransactionContext transactionContext, + {Map? customSamplingContext, + bool? bindToScope}) { return NoOpSentrySpan(); } } From 81babf74924f0ea7cd7074c3cd329d1bbee98bea Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Thu, 25 Nov 2021 12:16:09 +0100 Subject: [PATCH 5/6] Fix Flutter example using wrong sentry version --- flutter/example/pubspec.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/flutter/example/pubspec.yaml b/flutter/example/pubspec.yaml index bb1bd27afc..2fd1576045 100644 --- a/flutter/example/pubspec.yaml +++ b/flutter/example/pubspec.yaml @@ -11,8 +11,8 @@ environment: dependencies: flutter: sdk: flutter + sentry: sentry_flutter: - path: ../ universal_platform: ^1.0.0-nullsafety feedback: ^2.0.0 provider: ^6.0.0 @@ -20,6 +20,12 @@ dependencies: dev_dependencies: sentry_dart_plugin: ^1.0.0-alpha.4 +dependency_overrides: + sentry: + path: ../../dart + sentry_flutter: + path: ../ + flutter: uses-material-design: true From 2898fecf885c749ca8b06448f6b375d97d24438b Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Fri, 26 Nov 2021 10:28:25 +0100 Subject: [PATCH 6/6] Add changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 415a645cd6..51812e27c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +* Feat: Add logger name to `SentryLogger` and send errors in integrations to the registered logger (#641) + # 6.1.2 * Fix: Remove is Enum check to support older Dart versions (#635)