Skip to content
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

Add mechanism to Dio Http Client error #1114

Merged
merged 5 commits into from
Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,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

Expand Down
15 changes: 12 additions & 3 deletions dart/lib/src/http_client/failed_request_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions dart/lib/src/sentry_exception_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}

Expand All @@ -39,6 +43,7 @@ class SentryExceptionFactory {
if (frames.isNotEmpty) {
sentryStackTrace = SentryStackTrace(
frames: frames,
snapshot: snapshot,
);
}
}
Expand Down
9 changes: 8 additions & 1 deletion dart/lib/src/throwable_mechanism.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 4 additions & 2 deletions dart/test/http_client/failed_request_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions dart/test/sentry_exception_factory_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
6 changes: 5 additions & 1 deletion dio/lib/src/failed_request_interceptor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
9 changes: 8 additions & 1 deletion dio/test/failed_request_interceptor_test.dart
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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);
});
}

Expand Down