diff --git a/CHANGELOG.md b/CHANGELOG.md index 422f0c7e6f..cacddeb8f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Merging of integrations and packages ([#1111](https://github.com/getsentry/sentry-dart/pull/1111)) - Add missing `fragment` for HTTP Client Errors ([#1102](https://github.com/getsentry/sentry-dart/pull/1102)) - Sync user name and geo for Android ([#1102](https://github.com/getsentry/sentry-dart/pull/1102)) +- Add mechanism to Dio Http Client error ([#1114](https://github.com/getsentry/sentry-dart/pull/1114)) ### Dependencies diff --git a/dart/lib/src/http_client/failed_request_client.dart b/dart/lib/src/http_client/failed_request_client.dart index e5c7fbf38f..606b103bb6 100644 --- a/dart/lib/src/http_client/failed_request_client.dart +++ b/dart/lib/src/http_client/failed_request_client.dart @@ -126,8 +126,7 @@ class FailedRequestClient extends BaseClient { } else if (failedRequestStatusCodes.containsStatusCode(statusCode)) { // Capture an exception if the status code is considered bad capture = true; - reason = - 'Event was captured because the request status code was $statusCode'; + reason = 'HTTP Client Error with status code: $statusCode'; exception ??= SentryHttpClientError(reason); } if (capture) { @@ -184,7 +183,17 @@ class FailedRequestClient extends BaseClient { type: 'SentryHttpClient', description: reason, ); - final throwableMechanism = ThrowableMechanism(mechanism, exception); + + bool? snapshot; + if (exception is SentryHttpClientError) { + snapshot = true; + } + + final throwableMechanism = ThrowableMechanism( + mechanism, + exception, + snapshot: snapshot, + ); final event = SentryEvent( throwable: throwableMechanism, diff --git a/dart/lib/src/sentry_exception_factory.dart b/dart/lib/src/sentry_exception_factory.dart index b59ab6da18..e4a2f12561 100644 --- a/dart/lib/src/sentry_exception_factory.dart +++ b/dart/lib/src/sentry_exception_factory.dart @@ -17,9 +17,11 @@ class SentryExceptionFactory { }) { var throwable = exception; Mechanism? mechanism; + bool? snapshot; if (exception is ThrowableMechanism) { throwable = exception.throwable; mechanism = exception.mechanism; + snapshot = exception.snapshot; } if (throwable is Error) { @@ -29,6 +31,8 @@ class SentryExceptionFactory { // hence we check again if stackTrace is null and if not, read the current stack trace // but only if attachStacktrace is enabled if (_options.attachStacktrace) { + // TODO: snapshot=true if stackTrace is null + // Requires a major breaking change because of grouping stackTrace ??= StackTrace.current; } @@ -39,6 +43,7 @@ class SentryExceptionFactory { if (frames.isNotEmpty) { sentryStackTrace = SentryStackTrace( frames: frames, + snapshot: snapshot, ); } } diff --git a/dart/lib/src/throwable_mechanism.dart b/dart/lib/src/throwable_mechanism.dart index 2c8fd525ff..8a3932a701 100644 --- a/dart/lib/src/throwable_mechanism.dart +++ b/dart/lib/src/throwable_mechanism.dart @@ -4,10 +4,17 @@ import 'protocol/mechanism.dart'; class ThrowableMechanism implements Exception { final Mechanism _mechanism; final dynamic _throwable; + final bool? _snapshot; - ThrowableMechanism(this._mechanism, this._throwable); + ThrowableMechanism( + this._mechanism, + this._throwable, { + bool? snapshot, + }) : _snapshot = snapshot; Mechanism get mechanism => _mechanism; dynamic get throwable => _throwable; + + bool? get snapshot => _snapshot; } diff --git a/dart/test/http_client/failed_request_client_test.dart b/dart/test/http_client/failed_request_client_test.dart index b8044da108..48bfc7ab45 100644 --- a/dart/test/http_client/failed_request_client_test.dart +++ b/dart/test/http_client/failed_request_client_test.dart @@ -48,6 +48,7 @@ void main() { final mechanism = exception?.mechanism; expect(exception?.stackTrace, isNotNull); + expect(exception?.stackTrace!.snapshot, isNull); expect(mechanism?.type, 'SentryHttpClient'); final request = eventCall.request; @@ -101,14 +102,15 @@ void main() { expect(mechanism?.type, 'SentryHttpClient'); expect( mechanism?.description, - 'Event was captured because the request status code was 404', + 'HTTP Client Error with status code: 404', ); expect(exception?.type, 'SentryHttpClientError'); expect( exception?.value, - 'Exception: Event was captured because the request status code was 404', + 'Exception: HTTP Client Error with status code: 404', ); + expect(exception?.stackTrace?.snapshot, true); final request = eventCall.request; expect(request, isNotNull); diff --git a/dart/test/sentry_exception_factory_test.dart b/dart/test/sentry_exception_factory_test.dart index 9af95538c0..d6ace485cf 100644 --- a/dart/test/sentry_exception_factory_test.dart +++ b/dart/test/sentry_exception_factory_test.dart @@ -83,6 +83,28 @@ void main() { // skip on browser because [StackTrace.current] still returns null }, onPlatform: {'browser': Skip()}); + + test('reads the snapshot from the mechanism', () { + final error = StateError('test-error'); + final mechanism = Mechanism(type: 'Mechanism'); + final throwableMechanism = ThrowableMechanism( + mechanism, + error, + snapshot: true, + ); + + SentryException sentryException; + try { + throw throwableMechanism; + } catch (err, stackTrace) { + sentryException = fixture.getSut().getSentryException( + throwableMechanism, + stackTrace: stackTrace, + ); + } + + expect(sentryException.stackTrace!.snapshot, true); + }); } class CustomError extends Error {} diff --git a/dio/lib/src/failed_request_interceptor.dart b/dio/lib/src/failed_request_interceptor.dart index d9b6af30c8..e23dc6501d 100644 --- a/dio/lib/src/failed_request_interceptor.dart +++ b/dio/lib/src/failed_request_interceptor.dart @@ -13,8 +13,12 @@ class FailedRequestInterceptor extends Interceptor { DioError err, ErrorInterceptorHandler handler, ) async { + final mechanism = Mechanism(type: 'SentryDioClientAdapter'); + final throwableMechanism = ThrowableMechanism(mechanism, err); + _hub.getSpan()?.throwable = err; - await _hub.captureException(err); + + await _hub.captureException(throwableMechanism); handler.next(err); } diff --git a/dio/test/failed_request_interceptor_test.dart b/dio/test/failed_request_interceptor_test.dart index 26ff1e2598..0d58dfd516 100644 --- a/dio/test/failed_request_interceptor_test.dart +++ b/dio/test/failed_request_interceptor_test.dart @@ -1,4 +1,5 @@ import 'package:dio/dio.dart'; +import 'package:sentry/sentry.dart'; import 'package:sentry_dio/src/failed_request_interceptor.dart'; import 'package:test/test.dart'; @@ -14,13 +15,19 @@ void main() { test('interceptor send error', () async { final interceptor = fixture.getSut(); + final error = DioError(requestOptions: RequestOptions(path: '')); await interceptor.onError( - DioError(requestOptions: RequestOptions(path: '')), + error, fixture.errorInterceptorHandler, ); expect(fixture.errorInterceptorHandler.nextWasCalled, true); expect(fixture.hub.captureExceptionCalls.length, 1); + + final throwable = + fixture.hub.captureExceptionCalls.first.throwable as ThrowableMechanism; + expect(throwable.mechanism.type, 'SentryDioClientAdapter'); + expect(throwable.throwable, error); }); }