Skip to content

Feat: sentry and sentry_flutter null safety #335

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

Merged
merged 8 commits into from
Mar 8, 2021
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
5 changes: 2 additions & 3 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
# TODO: cedx/setup-dart@v2 doesn't work on Windows (doesn't add pub to the PATH?)
# os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-latest, macos-latest]
sdk: [beta, dev, stable]
sdk: [stable, beta, dev]
exclude:
# Bad state: Could not run tests with Observatory enabled. Try setting a different port with --port option.
- os: ubuntu-latest
Expand Down Expand Up @@ -83,8 +83,7 @@ jobs:
TOTAL_MAX: ${{ steps.analysis.outputs.total_max }}
run: |
PERCENTAGE=$(( $TOTAL * 100 / $TOTAL_MAX ))
# revert to 100 when we are nul-safety ready because of (http)
if (( $PERCENTAGE < 90 ))
if (( $PERCENTAGE < 100 ))
then
echo Score too low!
exit 1
Expand Down
8 changes: 2 additions & 6 deletions .github/workflows/flutter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ jobs:
target: ios
- os: windows-latest
target: ios
# No web on stable yet
- channel: stable
target: web
# macos-latest is taking hours due to limited resources
- os: macos-latest
target: android
Expand Down Expand Up @@ -87,8 +84,6 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
channel: 'beta'
- run: |
cd flutter
flutter pub get
Expand All @@ -110,7 +105,8 @@ jobs:
TOTAL_MAX: ${{ steps.analysis.outputs.total_max }}
run: |
PERCENTAGE=$(( $TOTAL * 100 / $TOTAL_MAX ))
if (( $PERCENTAGE < 90 ))
# 70 because pana does not understand null-safety yet
if (( $PERCENTAGE < 70 ))
then
echo Score too low!
exit 1
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

* Fix: Do not append stack trace to the exception if there are no frames
* Fix: Empty DSN disables the SDK and runs the App
* Refactoring: Migrate Sentry Dart to null safety
* Refactoring: Null safety on sentry_flutter (#337)

# 4.0.6

Expand Down
11 changes: 5 additions & 6 deletions dart/lib/src/http_client/sentry_http_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ import '../../sentry.dart';
// For example with Darts Stopwatch:
// https://api.dart.dev/stable/2.10.4/dart-core/Stopwatch-class.html
class SentryHttpClient extends BaseClient {
SentryHttpClient({Client client, Hub hub}) {
_hub = hub ?? HubAdapter();
_client = client ?? Client();
}
SentryHttpClient({Client? client, Hub? hub})
: _hub = hub ?? HubAdapter(),
_client = client ?? Client();

Client _client;
Hub _hub;
final Client _client;
final Hub _hub;

@override
Future<StreamedResponse> send(BaseRequest request) async {
Expand Down
192 changes: 65 additions & 127 deletions dart/lib/src/hub.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:collection';

import 'noop_sentry_client.dart';
import 'protocol.dart';
import 'scope.dart';
import 'sentry_client.dart';
Expand All @@ -18,8 +17,9 @@ class Hub {

final ListQueue<_StackItem> _stack = ListQueue();

/// if stack is empty, it throws IterableElementError.noElement()
_StackItem _peek() => _stack.isNotEmpty ? _stack.first : null;
// peek can never return null since Stack can be created only with an item and
// pop does not drop the last item.
_StackItem _peek() => _stack.first;

final SentryOptions _options;

Expand All @@ -35,11 +35,7 @@ class Hub {
}

static void _validateOptions(SentryOptions options) {
if (options == null) {
throw ArgumentError('SentryOptions is required.');
}

if (options.dsn?.isNotEmpty != true) {
if (options.dsn == null) {
throw ArgumentError('DSN is required.');
}
}
Expand Down Expand Up @@ -67,34 +63,23 @@ class Hub {
SentryLevel.warning,
"Instance is disabled and this 'captureEvent' call is a no-op.",
);
} else if (event == null) {
_options.logger(
SentryLevel.warning,
'captureEvent called with null parameter.',
);
} else {
final item = _peek();
if (item != null) {
try {
sentryId = await item.client.captureEvent(
event,
stackTrace: stackTrace,
scope: item.scope,
hint: hint,
);
} catch (err) {
_options.logger(
SentryLevel.error,
'Error while capturing event with id: ${event.eventId}, error: $err',
);
} finally {
_lastEventId = sentryId;
}
} else {

try {
sentryId = await item.client.captureEvent(
event,
stackTrace: stackTrace,
scope: item.scope,
hint: hint,
);
} catch (err) {
_options.logger(
SentryLevel.fatal,
'Stack peek was null when captureEvent',
SentryLevel.error,
'Error while capturing event with id: ${event.eventId}, error: $err',
);
} finally {
_lastEventId = sentryId;
}
}
return sentryId;
Expand All @@ -120,27 +105,21 @@ class Hub {
);
} else {
final item = _peek();
if (item != null) {
try {
sentryId = await item.client.captureException(
throwable,
stackTrace: stackTrace,
scope: item.scope,
hint: hint,
);
} catch (err) {
_options.logger(
SentryLevel.error,
'Error while capturing exception : $throwable',
);
} finally {
_lastEventId = sentryId;
}
} else {

try {
sentryId = await item.client.captureException(
throwable,
stackTrace: stackTrace,
scope: item.scope,
hint: hint,
);
} catch (err) {
_options.logger(
SentryLevel.fatal,
'Stack peek was null when captureException',
SentryLevel.error,
'Error while capturing exception : $throwable',
);
} finally {
_lastEventId = sentryId;
}
}

Expand All @@ -149,10 +128,10 @@ class Hub {

/// Captures the message.
Future<SentryId> captureMessage(
String message, {
SentryLevel level,
String template,
List<dynamic> params,
String? message, {
SentryLevel? level,
String? template,
List<dynamic>? params,
dynamic hint,
}) async {
var sentryId = SentryId.empty();
Expand All @@ -169,29 +148,23 @@ class Hub {
);
} else {
final item = _peek();
if (item != null) {
try {
sentryId = await item.client.captureMessage(
message,
level: level,
template: template,
params: params,
scope: item.scope,
hint: hint,
);
} catch (err) {
_options.logger(
SentryLevel.error,
'Error while capturing message with id: $message, error: $err',
);
} finally {
_lastEventId = sentryId;
}
} else {

try {
sentryId = await item.client.captureMessage(
message,
level: level,
template: template,
params: params,
scope: item.scope,
hint: hint,
);
} catch (err) {
_options.logger(
SentryLevel.fatal,
'Stack peek was null when captureMessage',
SentryLevel.error,
'Error while capturing message with id: $message, error: $err',
);
} finally {
_lastEventId = sentryId;
}
}
return sentryId;
Expand All @@ -204,21 +177,9 @@ class Hub {
SentryLevel.warning,
"Instance is disabled and this 'addBreadcrumb' call is a no-op.",
);
} else if (crumb == null) {
_options.logger(
SentryLevel.warning,
'addBreadcrumb called with null parameter.',
);
} else {
final item = _peek();
if (item != null) {
item.scope.addBreadcrumb(crumb, hint: hint);
} else {
_options.logger(
SentryLevel.fatal,
'Stack peek was null when addBreadcrumb',
);
}
item.scope.addBreadcrumb(crumb, hint: hint);
}
}

Expand All @@ -229,20 +190,8 @@ class Hub {
"Instance is disabled and this 'bindClient' call is a no-op.");
} else {
final item = _peek();
if (item != null) {
if (client != null) {
_options.logger(SentryLevel.debug, 'New client bound to scope.');
item.client = client;
} else {
_options.logger(SentryLevel.debug, 'NoOp client bound to scope.');
item.client = NoOpSentryClient();
}
} else {
_options.logger(
SentryLevel.fatal,
'Stack peek was null when bindClient',
);
}
_options.logger(SentryLevel.debug, 'New client bound to scope.');
item.client = client;
}
}

Expand Down Expand Up @@ -272,21 +221,16 @@ class Hub {
}

final item = _peek();
if (item != null) {
try {
item.client.close();
} catch (err) {
_options.logger(
SentryLevel.error,
'Error while closing the Hub, error: $err',
);
}
} else {

try {
item.client.close();
} catch (err) {
_options.logger(
SentryLevel.fatal,
'Stack peek was NULL when closing Hub',
SentryLevel.error,
'Error while closing the Hub, error: $err',
);
}

_isEnabled = false;
}
}
Expand All @@ -300,19 +244,13 @@ class Hub {
);
} else {
final item = _peek();
if (item != null) {
try {
callback(item.scope);
} catch (err) {
_options.logger(
SentryLevel.error,
"Error in the 'configureScope' callback, error: $err",
);
}
} else {

try {
callback(item.scope);
} catch (err) {
_options.logger(
SentryLevel.fatal,
'Stack peek was NULL when configureScope',
SentryLevel.error,
"Error in the 'configureScope' callback, error: $err",
);
}
}
Expand Down
8 changes: 4 additions & 4 deletions dart/lib/src/hub_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ class HubAdapter implements Hub {

@override
Future<SentryId> captureMessage(
String message, {
SentryLevel level,
String template,
List params,
String? message, {
SentryLevel? level,
String? template,
List? params,
dynamic hint,
}) =>
Sentry.captureMessage(
Expand Down
2 changes: 1 addition & 1 deletion dart/lib/src/isolate_error_integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'sentry_options.dart';
import 'throwable_mechanism.dart';

class IsolateErrorIntegration extends Integration {
RawReceivePort _receivePort;
late RawReceivePort _receivePort;

@override
FutureOr<void> call(Hub hub, SentryOptions options) async {
Expand Down
Loading