-
-
Notifications
You must be signed in to change notification settings - Fork 242
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: Improve Dio exception reports (#718)
- Loading branch information
Showing
9 changed files
with
437 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
library sentry_dio; | ||
|
||
export 'src/sentry_dio_extension.dart'; | ||
|
||
// Export the processor in case people want to use it standalone. | ||
// Normally one doesn't need to use it directly. | ||
export 'src/dio_event_processor.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
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.response] if present and also about | ||
/// the inner exceptions. | ||
class DioEventProcessor implements EventProcessor { | ||
/// This is an [EventProcessor], which improves crash reports of [DioError]s. | ||
DioEventProcessor(this._options, this._maxRequestBodySize); | ||
|
||
final SentryOptions _options; | ||
final MaxRequestBodySize _maxRequestBodySize; | ||
|
||
SentryExceptionFactory get _sentryExceptionFactory => | ||
// ignore: invalid_use_of_internal_member | ||
_options.exceptionFactory; | ||
|
||
@override | ||
FutureOr<SentryEvent?> apply(SentryEvent event, {dynamic hint}) { | ||
final dynamic dioError = event.throwable; | ||
if (dioError is! DioError) { | ||
return event; | ||
} | ||
|
||
// Don't override just parts of the original request. | ||
// Keep the original one or if there's none create one. | ||
event = event.copyWith(request: event.request ?? _requestFrom(dioError)); | ||
|
||
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) { | ||
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; | ||
} | ||
|
||
/// Remove the StackTrace from [dioError] so the message on Sentry looks | ||
/// much better. | ||
List<SentryException> _removeDioErrorStackTraceFromValue( | ||
List<SentryException> exceptions, | ||
DioError dioError, | ||
) { | ||
var dioSentryException = exceptions | ||
.where((element) => element.type == dioError.runtimeType.toString()) | ||
.first; | ||
|
||
final exceptionIndex = exceptions.indexOf(dioSentryException); | ||
exceptions.remove(dioSentryException); | ||
|
||
// Remove error and stacktrace, so that the DioError value doesn't | ||
// include the chained exception. | ||
dioError.stackTrace = null; | ||
dioError.error = null; | ||
|
||
dioSentryException = dioSentryException.copyWith( | ||
value: dioError.toString(), | ||
); | ||
|
||
exceptions.insert(exceptionIndex, dioSentryException); | ||
|
||
return exceptions; | ||
} | ||
|
||
SentryRequest? _requestFrom(DioError dioError) { | ||
final options = dioError.requestOptions; | ||
// As far as I can tell there's no way to get the uri without the query part | ||
// so we replace it with an empty string. | ||
final urlWithoutQuery = options.uri.replace(query: '').toString(); | ||
|
||
final query = options.uri.query.isEmpty ? null : options.uri.query; | ||
|
||
final headers = options.headers | ||
.map((key, dynamic value) => MapEntry(key, value?.toString() ?? '')); | ||
|
||
return SentryRequest( | ||
method: options.method, | ||
headers: _options.sendDefaultPii ? headers : null, | ||
url: urlWithoutQuery, | ||
queryString: query, | ||
cookies: _options.sendDefaultPii | ||
? options.headers['Cookie']?.toString() | ||
: null, | ||
data: _getRequestData(dioError.response?.data), | ||
); | ||
} | ||
|
||
/// Returns the request data, if possible according to the users settings. | ||
/// Type checks are based on DIOs [ResponseType]. | ||
Object? _getRequestData(dynamic data) { | ||
if (!_options.sendDefaultPii) { | ||
return null; | ||
} | ||
if (data is String) { | ||
if (_maxRequestBodySize.shouldAddBody(data.codeUnits.length)) { | ||
return data; | ||
} | ||
} else if (data is List<int>) { | ||
if (_maxRequestBodySize.shouldAddBody(data.length)) { | ||
return data; | ||
} | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.