-
-
Notifications
You must be signed in to change notification settings - Fork 277
Feat: Error Cause Extractor #1198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
88e39d2
ba00651
81394d1
620e917
1c7d835
2584bad
e8384ca
b4fb217
077aff4
a1acbf3
fcd1a38
34e9e8a
84f1104
0c15cd2
413ddd0
4af4d07
5159821
983bdf3
bde4df0
4bd8ad4
406e493
61900d4
5d78242
b2abac7
2e65a9e
4f37d19
72cfb5b
db64952
dcbc09f
4938f3a
cce32de
22196e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import 'package:dio/dio.dart'; | ||
| import 'package:sentry/sentry.dart'; | ||
|
|
||
| /// Extracts the inner exception and stacktrace from [DioError] | ||
| class DioErrorExtractor extends ExceptionCauseExtractor<DioError> { | ||
| @override | ||
| ExceptionCause? cause(DioError error) { | ||
| if (error.stackTrace == null) { | ||
| return null; | ||
| } | ||
| return ExceptionCause( | ||
| error.error ?? 'DioError inner stacktrace', | ||
| error.stackTrace, | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,25 +2,16 @@ import 'dart:async'; | |
|
|
||
| import 'package:dio/dio.dart'; | ||
| import 'package:sentry/sentry.dart'; | ||
| // ignore: implementation_imports | ||
| import 'package:sentry/src/sentry_exception_factory.dart'; | ||
|
|
||
| /// This is an [EventProcessor], which improves crash reports of [DioError]s. | ||
| /// It adds information about [DioError.requestOptions] if present and also about | ||
| /// the inner exceptions. | ||
| class DioEventProcessor implements EventProcessor { | ||
| // Because of obfuscation, we need to dynamically get the name | ||
| static final _dioErrorType = (DioError).toString(); | ||
|
|
||
| /// This is an [EventProcessor], which improves crash reports of [DioError]s. | ||
| DioEventProcessor(this._options); | ||
|
|
||
| final SentryOptions _options; | ||
|
|
||
| SentryExceptionFactory get _sentryExceptionFactory => | ||
| // ignore: invalid_use_of_internal_member | ||
| _options.exceptionFactory; | ||
|
|
||
| @override | ||
| FutureOr<SentryEvent?> apply(SentryEvent event, {Hint? hint}) { | ||
| final dynamic dioError = event.throwable; | ||
denrase marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
@@ -41,41 +32,20 @@ class DioEventProcessor implements EventProcessor { | |
| contexts: contexts, | ||
| ); | ||
|
|
||
| final innerDioStackTrace = dioError.stackTrace; | ||
| final innerDioErrorException = dioError.error as Object?; | ||
|
|
||
| // If the inner errors stacktrace is null, | ||
| // there's nothing to create chained exception | ||
| if (innerDioStackTrace == null) { | ||
| // there is no chained exception | ||
| if (dioError.stackTrace == null) { | ||
| return event; | ||
| } | ||
|
|
||
| try { | ||
| final innerException = _sentryExceptionFactory.getSentryException( | ||
| innerDioErrorException ?? 'DioError inner stacktrace', | ||
| stackTrace: innerDioStackTrace, | ||
| ); | ||
|
|
||
| final exceptions = _removeDioErrorStackTraceFromValue( | ||
| List<SentryException>.from(event.exceptions ?? <SentryException>[]), | ||
| dioError, | ||
| ); | ||
|
|
||
| return event.copyWith( | ||
| exceptions: [ | ||
| innerException, | ||
| ...exceptions, | ||
| ], | ||
| ); | ||
| } catch (e, stackTrace) { | ||
| _options.logger( | ||
| SentryLevel.debug, | ||
| 'Could not convert DioError to SentryException', | ||
| exception: e, | ||
| stackTrace: stackTrace, | ||
| ); | ||
| } | ||
| return event; | ||
| final exceptions = _removeDioErrorStackTraceFromValue( | ||
| List<SentryException>.from(event.exceptions ?? <SentryException>[]), | ||
| dioError, | ||
| ); | ||
|
||
|
|
||
| return event.copyWith( | ||
| exceptions: exceptions.reversed.toList(), // Inner before DioError | ||
| ); | ||
denrase marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// Remove the StackTrace from [dioError] so the message on Sentry looks | ||
|
|
@@ -85,7 +55,7 @@ class DioEventProcessor implements EventProcessor { | |
| DioError dioError, | ||
| ) { | ||
| final dioSentryExceptions = | ||
| exceptions.where((element) => element.type == _dioErrorType); | ||
| exceptions.where((element) => element.throwable is DioError); | ||
|
|
||
| if (dioSentryExceptions.isEmpty) { | ||
| return exceptions; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import 'package:dio/dio.dart'; | ||
| import 'package:sentry/sentry.dart'; | ||
| import 'dio_error_extractor.dart'; | ||
| import 'dio_event_processor.dart'; | ||
| import 'failed_request_interceptor.dart'; | ||
| import 'sentry_transformer.dart'; | ||
|
|
@@ -19,6 +20,9 @@ extension SentryDioExtension on Dio { | |
| // ignore: invalid_use_of_internal_member | ||
| final options = hub.options; | ||
|
|
||
| // Add to get inner exception & stacktrace | ||
| options.addExceptionCauseExtractor(DioErrorExtractor()); | ||
|
Comment on lines
+23
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to do something similar to the lines below here, L27-28, only adding the dio extractor if already not there, just for defense programming, otherwise calling this extension multiple times would add multiple extractors, maybe causing side effects. |
||
|
|
||
| // Add DioEventProcessor when it's not already present | ||
| if (options.eventProcessors.whereType<DioEventProcessor>().isEmpty) { | ||
| options.sdk.addIntegration('sentry_dio'); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import 'package:dio/dio.dart'; | ||
| import 'package:sentry_dio/src/dio_error_extractor.dart'; | ||
| import 'package:test/test.dart'; | ||
|
|
||
| void main() { | ||
| late Fixture fixture; | ||
|
|
||
| setUp(() { | ||
| fixture = Fixture(); | ||
| }); | ||
|
|
||
| test('$DioErrorExtractor extracts error and stacktrace', () { | ||
| final sut = fixture.getSut(); | ||
| final exception = Exception('foo bar'); | ||
| final stacktrace = StackTrace.current; | ||
|
|
||
| final dioError = DioError( | ||
| error: exception, | ||
| requestOptions: RequestOptions(path: '/foo/bar'), | ||
| )..stackTrace = stacktrace; | ||
|
|
||
| final cause = sut.cause(dioError); | ||
|
|
||
| expect(cause?.exception, exception); | ||
| expect(cause?.stackTrace, stacktrace); | ||
| }); | ||
|
|
||
| test('$DioErrorExtractor extracts stacktrace only', () { | ||
| final sut = fixture.getSut(); | ||
| final stacktrace = StackTrace.current; | ||
|
|
||
| final dioError = DioError( | ||
| requestOptions: RequestOptions(path: '/foo/bar'), | ||
| )..stackTrace = stacktrace; | ||
|
|
||
| final cause = sut.cause(dioError); | ||
|
|
||
| expect(cause?.exception, 'DioError inner stacktrace'); | ||
| expect(cause?.stackTrace, stacktrace); | ||
| }); | ||
|
|
||
| test('$DioErrorExtractor extracts nothing with missing stacktrace', () { | ||
| final sut = fixture.getSut(); | ||
| final exception = Exception('foo bar'); | ||
|
|
||
| final dioError = DioError( | ||
| error: exception, | ||
| requestOptions: RequestOptions(path: '/foo/bar'), | ||
| ); | ||
|
|
||
| final cause = sut.cause(dioError); | ||
|
|
||
| expect(cause, isNull); | ||
| }); | ||
| } | ||
|
|
||
| class Fixture { | ||
| DioErrorExtractor getSut() { | ||
| return DioErrorExtractor(); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.