Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6a5d2c3
hub interface & instanciation
rxlabz Oct 15, 2020
4f01775
- hub capture event
rxlabz Oct 15, 2020
ea5c55f
- hub capture exception
rxlabz Oct 15, 2020
bd09da0
hub captureMessage
rxlabz Oct 15, 2020
c5317a4
fix some tests
rxlabz Oct 15, 2020
6a8910e
fix more tests
rxlabz Oct 15, 2020
f5d3dd0
changelog
rxlabz Oct 15, 2020
f102ac3
fix all tests
rxlabz Oct 15, 2020
055b9b0
feedbacks
rxlabz Oct 15, 2020
370d2d5
fix test : revert to ArgumentError.notNull('options')
rxlabz Oct 15, 2020
974d1f6
remove required hub methods
rxlabz Oct 15, 2020
c8c1df0
implement `Hub.close()`
rxlabz Oct 16, 2020
6c6c649
feedbacks : remove the IHub interface + minors
rxlabz Oct 16, 2020
a924040
Hub.configureScope
rxlabz Oct 16, 2020
823c91c
Hub.clone and Scope.clone
rxlabz Oct 16, 2020
b04d67c
remove the non required scope methods
rxlabz Oct 16, 2020
37cf83c
replace the ListQueue _stack by a DoubleLinkedQueue
rxlabz Oct 16, 2020
b9f9f9e
rename `response` to `sentryId`
rxlabz Oct 16, 2020
5067a36
serialize the potential error stackTrace
rxlabz Oct 16, 2020
54729ba
Revert the DoubleLinkedQueue to a resourceless ListQueue
rxlabz Oct 16, 2020
729d6bc
fix a json serialization test
rxlabz Oct 16, 2020
4ea1537
add a const SentryId.emptyId
rxlabz Oct 16, 2020
5932534
don't assign a new lastEventId if the client capture method is not ca…
rxlabz Oct 16, 2020
97f6c53
feedbacks:
rxlabz Oct 16, 2020
e89c488
feedbacks:
rxlabz Oct 16, 2020
647f1b0
simplify the captureMessage API
rxlabz Oct 19, 2020
91f709a
capture message
rxlabz Oct 19, 2020
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 @@ -20,6 +20,7 @@
- new static API : Sentry.init(), Sentry.captureEvent() #108
- expect a sdkName based on the test platform #105
- Added Scope and Breadcrumb ring buffer #109
- Added Hub ring buffer #113

# `package:sentry` changelog

Expand Down
12 changes: 2 additions & 10 deletions dart/example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ Future<void> main(List<String> rawArgs) async {
// Sends a full Sentry event payload to show the different parts of the UI.
final response = await Sentry.captureEvent(event);

if (response.isSuccessful) {
print('SUCCESS\nid: ${response.eventId}');
} else {
print('FAILURE: ${response.error}');
}
print('SentryId : ${response.id}');

try {
await foo();
Expand All @@ -41,11 +37,7 @@ Future<void> main(List<String> rawArgs) async {
stackTrace: stackTrace,
);

if (response.isSuccessful) {
print('SUCCESS\nid: ${response.eventId}');
} else {
print('FAILURE: ${response.error}');
}
print('SentryId : ${response.id}');
} finally {
await Sentry.close();
}
Expand Down
36 changes: 22 additions & 14 deletions dart/lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:convert';

import 'package:http/http.dart';
import 'package:meta/meta.dart';
import 'package:sentry/sentry.dart';

import 'client_stub.dart'
if (dart.library.html) 'browser_client.dart'
Expand Down Expand Up @@ -136,9 +137,10 @@ abstract class SentryClient {
}

/// Reports an [event] to Sentry.io.
Future<SentryResponse> captureEvent({
@required Event event,
Future<SentryId> captureEvent(
Event event, {
StackFrameFilter stackFrameFilter,
Scope scope,
}) async {
final now = _clock();
var authHeader = 'Sentry sentry_version=6, sentry_client=$clientId, '
Expand Down Expand Up @@ -183,27 +185,33 @@ abstract class SentryClient {
);

if (response.statusCode != 200) {
var errorMessage = 'Sentry.io responded with HTTP ${response.statusCode}';
/*var errorMessage = 'Sentry.io responded with HTTP ${response.statusCode}';
if (response.headers['x-sentry-error'] != null) {
errorMessage += ': ${response.headers['x-sentry-error']}';
}
return SentryResponse.failure(errorMessage);
}*/
return SentryId.empty();
}

final eventId = '${json.decode(response.body)['id']}';
return SentryResponse.success(eventId: eventId);
final eventId = json.decode(response.body)['id'];
return eventId != null ? SentryId(eventId) : SentryId.empty();
}

/// Reports the [exception] and optionally its [stackTrace] to Sentry.io.
Future<SentryResponse> captureException({
@required dynamic exception,
dynamic stackTrace,
}) {
/// Reports the [throwable] and optionally its [stackTrace] to Sentry.io.
Future<SentryId> captureException(dynamic throwable, {dynamic stackTrace}) {
final event = Event(
exception: exception,
exception: throwable,
stackTrace: stackTrace,
);
return captureEvent(event: event);
return captureEvent(event);
}

/// Reports the [message]
Future<SentryId> captureMessage(
Message message, {
SeverityLevel level = SeverityLevel.info,
}) {
final event = Event(message: message, level: level);
return captureEvent(event);
}

Future<void> close() async {
Expand Down
297 changes: 297 additions & 0 deletions dart/lib/src/hub.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
import 'dart:async';
import 'dart:collection';

import 'client.dart';
import 'noop_client.dart';
import 'protocol.dart';
import 'scope.dart';
import 'sentry_options.dart';

typedef ScopeCallback = void Function(Scope);

/// SDK API contract which combines a client and scope management
class Hub {
static SentryClient _getClient({SentryOptions fromOptions}) {
return SentryClient(
dsn: fromOptions.dsn,
environmentAttributes: fromOptions.environmentAttributes,
compressPayload: fromOptions.compressPayload,
httpClient: fromOptions.httpClient,
clock: fromOptions.clock,
uuidGenerator: fromOptions.uuidGenerator,
);
}

final ListQueue<_StackItem> _stack;

final SentryOptions _options;

factory Hub(SentryOptions options) {
_validateOptions(options);

return Hub._(options);
}

Hub._(SentryOptions options)
: _options = options,
_stack = ListQueue() {
_stack.add(_StackItem(_getClient(fromOptions: options), Scope(_options)));
_isEnabled = true;
}

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

if (options.dsn == null) {
throw ArgumentError.notNull('options.dsn');
}
}

bool _isEnabled = false;

/// Check if the Hub is enabled/active.
bool get isEnabled => _isEnabled;

SentryId _lastEventId;

/// Last event id recorded in the current scope
SentryId get lastEventId => _lastEventId;

/// Captures the event.
Future<SentryId> captureEvent(Event event) async {
var sentryId = SentryId.empty();

if (!_isEnabled) {
_options.logger(
SeverityLevel.warning,
"Instance is disabled and this 'captureEvent' call is a no-op.",
);
} else if (event == null) {
_options.logger(
SeverityLevel.warning,
'captureEvent called with null parameter.',
);
} else {
final item = _stack.last;
if (item != null) {
try {
sentryId = await item.client.captureEvent(event, scope: item.scope);
} catch (err) {
/* TODO add Event.id */
_options.logger(
SeverityLevel.error,
'Error while capturing event with id: ${event}',
);
}
} else {
_options.logger(
SeverityLevel.fatal,
'Stack peek was null when captureEvent',
);
}
}
_lastEventId = sentryId;
return sentryId;
}

/// Captures the exception
Future<SentryId> captureException(
dynamic throwable, {
dynamic stackTrace,
}) async {
var sentryId = SentryId.empty();

if (!_isEnabled) {
_options.logger(
SeverityLevel.warning,
"Instance is disabled and this 'captureException' call is a no-op.",
);
} else if (throwable == null) {
_options.logger(
SeverityLevel.warning,
'captureException called with null parameter.',
);
} else {
final item = _stack.last;
if (item != null) {
try {
// TODO pass the scope
sentryId = await item.client.captureException(
throwable,
stackTrace: stackTrace,
);
} catch (err) {
_options.logger(
SeverityLevel.error,
'Error while capturing exception : ${throwable}',
);
}
} else {
_options.logger(
SeverityLevel.fatal,
'Stack peek was null when captureException',
);
}
}
_lastEventId = sentryId;
return sentryId;
}

/// Captures the message.
Future<SentryId> captureMessage(
Message message, {
SeverityLevel level = SeverityLevel.info,
}) async {
var sentryId = SentryId.empty();

if (!_isEnabled) {
_options.logger(
SeverityLevel.warning,
"Instance is disabled and this 'captureMessage' call is a no-op.",
);
} else if (message == null) {
_options.logger(
SeverityLevel.warning,
'captureMessage called with null parameter.',
);
} else {
final item = _stack.last;
if (item != null) {
try {
// TODO pass the scope
sentryId = await item.client.captureMessage(message, level: level);
} catch (err) {
_options.logger(
SeverityLevel.error,
'Error while capturing message with id: ${message}',
);
}
} else {
_options.logger(
SeverityLevel.fatal,
'Stack peek was null when captureMessage',
);
}
}
_lastEventId = sentryId;
return sentryId;
}

/// Binds a different client to the hub
void bindClient(SentryClient client) {
if (!_isEnabled) {
_options.logger(SeverityLevel.warning,
"Instance is disabled and this 'bindClient' call is a no-op.");
} else {
final item = _stack.last;
if (item != null) {
if (client != null) {
_options.logger(SeverityLevel.debug, 'New client bound to scope.');
item.client = client;
} else {
_options.logger(SeverityLevel.debug, 'NoOp client bound to scope.');
item.client = NoOpSentryClient();
}
} else {
_options.logger(
SeverityLevel.fatal,
'Stack peek was null when bindClient',
);
}
}
}

/// Clones the Hub
Hub clone() {
if (!_isEnabled) {
_options..logger(SeverityLevel.warning, 'Disabled Hub cloned.');
}
final clone = Hub(_options);
for (final item in _stack) {
clone._stack.add(_StackItem(item.client, item.scope.clone()));
}
return clone;
}

/// Flushes out the queue for up to timeout seconds and disable the Hub.
void close() {
if (!_isEnabled) {
_options.logger(
SeverityLevel.warning,
"Instance is disabled and this 'close' call is a no-op.",
);
} else {
final item = _stack.last;
if (item != null) {
try {
item.client.close();
} catch (err) {
_options.logger(
SeverityLevel.error,
'Error while closing the Hub.',
);
}
} else {
_options.logger(
SeverityLevel.fatal,
'Stack peek was NULL when closing Hub',
);
}
_isEnabled = false;
}
}

/// Configures the scope through the callback.
void configureScope(ScopeCallback callback) {
if (!_isEnabled) {
_options.logger(
SeverityLevel.warning,
"Instance is disabled and this 'configureScope' call is a no-op.",
);
} else {
final item = _stack.last;
if (item != null) {
try {
callback(item.scope);
} catch (err) {
_options.logger(
SeverityLevel.error,
"Error in the 'configureScope' callback.",
);
}
} else {
_options.logger(
SeverityLevel.fatal,
'Stack peek was NULL when configureScope',
);
}
}
}

/// Runs the callback with a new scope which gets dropped at the end
void pushScope() {
// TODO: implement popScope
throw UnimplementedError();
}

void popScope() {
// TODO: implement popScope
throw UnimplementedError();
}

/// Runs the callback with a new scope which gets dropped at the end
void withScope(ScopeCallback callback) {
// TODO: implement withScope
throw UnimplementedError();
}
}

class _StackItem {
SentryClient client;

final Scope scope;

_StackItem(this.client, this.scope);
}
Loading