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 markUnhandledAsFatal to SentryOptions #1720

Closed
Closed
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 8.0.0

### Features

- Add `markUnhandledAsFatal` to `SentryOptions` ([#1720](https://github.com/getsentry/sentry-dart/pull/1720))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 🚫 The changelog entry seems to be part of an already released section ## 8.0.0.
    Consider moving the entry to the ## Unreleased section, please.


### Breaking Changes

- Mark exceptions not handled by the user as `handled: false` ([#1535](https://github.com/getsentry/sentry-dart/pull/1535))
Expand Down
3 changes: 2 additions & 1 deletion dart/lib/src/run_zoned_guarded_integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ class RunZonedGuardedIntegration extends Integration<SentryOptions> {

final event = SentryEvent(
throwable: throwableMechanism,
level: SentryLevel.fatal,
level:
options.markUnhandledAsFatal ? SentryLevel.fatal : SentryLevel.error,
timestamp: hub.options.clock(),
);

Expand Down
5 changes: 3 additions & 2 deletions dart/lib/src/sentry_isolate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ class SentryIsolate {
// Isolate errors don't crash the app, but is not handled by the user.
final mechanism = Mechanism(type: 'isolateError', handled: false);
final throwableMechanism = ThrowableMechanism(mechanism, throwable);

final event = SentryEvent(
throwable: throwableMechanism,
level: SentryLevel.fatal,
level: hub.options.markUnhandledAsFatal
? SentryLevel.fatal
: SentryLevel.error,
timestamp: hub.options.clock(),
);

Expand Down
7 changes: 6 additions & 1 deletion dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'dart:developer';
import 'package:meta/meta.dart';
import 'package:http/http.dart';

import '../sentry.dart';
import '../sentry_io.dart';
import 'client_reports/client_report_recorder.dart';
import 'client_reports/noop_client_report_recorder.dart';
import 'sentry_exception_factory.dart';
Expand Down Expand Up @@ -358,6 +358,11 @@ class SentryOptions {
@internal
bool devMode = false;

/// Unhandled exceptions that the SDK automatically reports, for example in
/// [SentryIsolate], have `level` [SentryLevel.fatal] set per default.
/// Settings this to `false` will set the `level` to [SentryLevel.error].
bool markUnhandledAsFatal = true;

SentryOptions({this.dsn, PlatformChecker? checker}) {
if (checker != null) {
platformChecker = checker;
Expand Down
17 changes: 17 additions & 0 deletions dart/test/run_zoned_guarded_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ void main() {

expect(onErrorCalled, true);
});

test('sets level to error instead of fatal', () async {
final exception = StateError('error');
final stackTrace = StackTrace.current;

final hub = Hub(fixture.options);
final client = MockSentryClient();
hub.bindClient(client);

final sut = fixture.getSut(runner: () async {});

fixture.options.markUnhandledAsFatal = false;
await sut.captureError(hub, fixture.options, exception, stackTrace);

final capturedEvent = client.captureEventCalls.last.event;
expect(capturedEvent.level, SentryLevel.error);
});
});
}

Expand Down
6 changes: 2 additions & 4 deletions dart/test/sentry_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -847,8 +847,7 @@ void main() {
expect(capturedEvent.user?.ipAddress, '{{auto}}');
});

test('event has a user with IP address',
() async {
test('event has a user with IP address', () async {
final client = fixture.getSut(sendDefaultPii: true);

await client.captureEvent(fakeEvent);
Expand All @@ -864,8 +863,7 @@ void main() {
expect(capturedEvent.user?.email, fakeEvent.user!.email);
});

test('event has a user without IP address',
() async {
test('event has a user without IP address', () async {
final client = fixture.getSut(sendDefaultPii: true);

final event = fakeEvent.copyWith(user: fakeUser);
Expand Down
21 changes: 18 additions & 3 deletions dart/test/sentry_isolate_test.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
@TestOn('vm')

import 'package:sentry/src/hub.dart';
import 'package:sentry/src/protocol/span_status.dart';
import 'package:sentry/sentry.dart';
import 'package:sentry/src/sentry_isolate.dart';
import 'package:sentry/src/sentry_options.dart';
import 'package:test/test.dart';

import 'mocks.dart';
Expand Down Expand Up @@ -48,6 +46,23 @@ void main() {

await span?.finish();
});

test('sets level to error instead of fatal', () async {
final exception = StateError('error');
final stackTrace = StackTrace.current.toString();

final hub = Hub(fixture.options);
final client = MockSentryClient();
hub.bindClient(client);

fixture.options.markUnhandledAsFatal = false;

await SentryIsolate.handleIsolateError(
hub, [exception.toString(), stackTrace]);

final capturedEvent = client.captureEventCalls.last.event;
expect(capturedEvent.level, SentryLevel.error);
});
});
}

Expand Down
4 changes: 3 additions & 1 deletion flutter/lib/src/integrations/flutter_error_integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ class FlutterErrorIntegration implements Integration<SentryFlutterOptions> {

var event = SentryEvent(
throwable: throwableMechanism,
level: SentryLevel.fatal,
level: options.markUnhandledAsFatal
? SentryLevel.fatal
: SentryLevel.error,
contexts: flutterErrorDetails.isNotEmpty
? (Contexts()..['flutter_error_details'] = flutterErrorDetails)
: null,
Expand Down
1 change: 1 addition & 0 deletions flutter/lib/src/sentry_flutter_options.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:meta/meta.dart';
import 'package:sentry/sentry.dart';
import 'package:flutter/widgets.dart';
import 'package:sentry/sentry_io.dart';

import 'binding_wrapper.dart';
import 'renderer/renderer.dart';
Expand Down
14 changes: 14 additions & 0 deletions flutter/test/integrations/flutter_error_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,20 @@ void main() {

await span?.finish();
});

test('captures error with level error', () async {
final exception = StateError('error');

fixture.options.markUnhandledAsFatal = false;

_reportError(exception: exception);

final event = verify(
await fixture.hub.captureEvent(captureAny, hint: anyNamed('hint')),
).captured.first as SentryEvent;

expect(event.level, SentryLevel.error);
});
});
}

Expand Down