Skip to content
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
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,19 @@
- remove the `package:args` dependency #94
- move the `package:pedantic` to dev depencies #94
- Added GH Action Changelog verifier #95
- Added GH Action (CI) for Dart
- Added GH Action (CI) for Dart #97
- new Dart code file structure #96
- Base the sdk name on the platform (`sentry.dart` for io & flutter, `sentry.dart.browser` in a browser context) #103
- Single changelog and readme for both packages #105
- 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 to SDK #113
- Ref: Hub passes the Scope to SentryClient
- Ref: Hub passes the Scope to SentryClient #114
- Feature: sentry options #116
- Ref: SentryId generates UUID
- Ref: Event now is SentryEvent and added GPU
- Ref: SentryId generates UUID #119
- Ref: Event now is SentryEvent and added GPU #121
- Feat: before breadcrumb and scope ref. #122

# `package:sentry` changelog

Expand Down
64 changes: 38 additions & 26 deletions dart/lib/src/scope.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,14 @@ import 'sentry_options.dart';
/// Scope data to be sent with the event
class Scope {
/// How important this event is.
SentryLevel _level;

SentryLevel get level => _level;

set level(SentryLevel level) {
_level = level;
}
SentryLevel level;

/// The name of the transaction which generated this event,
/// for example, the route name: `"/users/<username>/"`.
String _transaction;

String get transaction => _transaction;

set transaction(String transaction) {
_transaction = transaction;
}
String transaction;

/// Information about the current user.
User _user;

User get user => _user;

set user(User user) {
_user = user;
}
User user;

/// Used to deduplicate events by grouping ones with the same fingerprint
/// together.
Expand Down Expand Up @@ -71,21 +53,38 @@ class Scope {

Map<String, dynamic> get extra => Map.unmodifiable(_extra);

// TODO: EventProcessors, Contexts, BeforeBreadcrumbCallback, Breadcrumb Hint, clone
// TODO: Contexts

/// Scope's event processor list
final List<EventProcessor> _eventProcessors = [];

List<EventProcessor> get eventProcessors =>
List.unmodifiable(_eventProcessors);

final SentryOptions _options;

Scope(this._options) : assert(_options != null, 'SentryOptions is required');

/// Adds a breadcrumb to the breadcrumbs queue
void addBreadcrumb(Breadcrumb breadcrumb) {
void addBreadcrumb(Breadcrumb breadcrumb, {dynamic hint}) {
assert(breadcrumb != null, "Breadcrumb can't be null");

// bail out if maxBreadcrumbs is zero
if (_options.maxBreadcrumbs == 0) {
return;
}

// run before breadcrumb callback if set
if (_options.beforeBreadcrumbCallback != null) {
breadcrumb = _options.beforeBreadcrumbCallback(breadcrumb, hint);

if (breadcrumb == null) {
_options.logger(
SentryLevel.info, 'Breadcrumb was dropped by beforeBreadcrumb');
return;
}
}

// remove first item if list if full
if (_breadcrumbs.length >= _options.maxBreadcrumbs &&
_breadcrumbs.isNotEmpty) {
Expand All @@ -100,15 +99,23 @@ class Scope {
_breadcrumbs.clear();
}

/// Adds an event processor
void addEventProcessor(EventProcessor eventProcessor) {
assert(eventProcessor != null, "EventProcessor can't be null");

_eventProcessors.add(eventProcessor);
}

/// Resets the Scope to its default state
void clear() {
clearBreadcrumbs();
_level = null;
_transaction = null;
_user = null;
level = null;
transaction = null;
user = null;
_fingerprint = null;
_tags.clear();
_extra.clear();
_eventProcessors.clear();
}

/// Sets a tag to the Scope
Expand All @@ -135,6 +142,7 @@ class Scope {
/// Removes an extra from the Scope
void removeExtra(String key) => _extra.remove(key);

/// Clones the current Scope
Scope clone() {
final clone = Scope(_options)
..user = user
Expand All @@ -153,6 +161,10 @@ class Scope {
clone.addBreadcrumb(breadcrumb);
}

for (final eventProcessor in _eventProcessors) {
Copy link
Member

Choose a reason for hiding this comment

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

Since we're in the class, what about a private constructor or factory method that takes all fields as named args?
Then we could just clone all lists etc like Scope({eventProcessors = [...this.eventprocessors]) and avoid tall these for

Copy link
Contributor Author

@marandaneto marandaneto Oct 21, 2020

Choose a reason for hiding this comment

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

mm yeah but rethinking this right now, maybe it'd make sense to just do a copyWith and follow the convention? tbh I just added the missing field in the existent method, I will check whats the state of the art for cloning objects on Dart.
Factory is public afaik, but a private ctor would work as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

https://pub.dev/documentation/copyable/latest/#examples
using the copyable interface seems to be the standard

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will add this as a task on our project, so I can merge this now

Copy link
Contributor

Choose a reason for hiding this comment

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

copyWith is probably the "most standard way" ( used a lot in Flutter source code, and in https://pub.dev/packages/freezed, a popular immutable package)

clone.addEventProcessor(eventProcessor);
}

return clone;
}
}
36 changes: 35 additions & 1 deletion dart/test/scope_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ void main() {
expect(sut.breadcrumbs.last, breadcrumb);
});

test('Executes and drops $Breadcrumb', () {
final sut = fixture.getSut(
beforeBreadcrumbCallback: fixture.beforeBreadcrumbCallback,
);

final breadcrumb = Breadcrumb('test log', DateTime.utc(2019));
sut.addBreadcrumb(breadcrumb);

expect(sut.breadcrumbs.length, 0);
});

test('adds $EventProcessor', () {
final sut = fixture.getSut();

sut.addEventProcessor(fixture.processor);

expect(sut.eventProcessors.last, fixture.processor);
});

test('respects max $Breadcrumb', () {
final maxBreadcrumbs = 2;
final sut = fixture.getSut(maxBreadcrumbs: maxBreadcrumbs);
Expand Down Expand Up @@ -149,6 +168,8 @@ void main() {
sut.setTag('test', 'test');
sut.setExtra('test', 'test');

sut.addEventProcessor(fixture.processor);

sut.clear();

expect(sut.breadcrumbs.length, 0);
Expand All @@ -164,6 +185,8 @@ void main() {
expect(sut.tags.length, 0);

expect(sut.extra.length, 0);

expect(sut.eventProcessors.length, 0);
});

test('clones', () {
Expand All @@ -175,13 +198,24 @@ void main() {
expect(sut.tags, clone.tags);
expect(sut.breadcrumbs, clone.breadcrumbs);
expect(ListEquality().equals(sut.fingerprint, clone.fingerprint), true);
expect(ListEquality().equals(sut.eventProcessors, clone.eventProcessors),
true);
});
}

class Fixture {
Scope getSut({int maxBreadcrumbs = 100}) {
Scope getSut({
int maxBreadcrumbs = 100,
BeforeBreadcrumbCallback beforeBreadcrumbCallback,
}) {
final options = SentryOptions();
options.maxBreadcrumbs = maxBreadcrumbs;
options.beforeBreadcrumbCallback = beforeBreadcrumbCallback;
return Scope(options);
}

SentryEvent processor(SentryEvent event, dynamic hint) => null;

Breadcrumb beforeBreadcrumbCallback(Breadcrumb breadcrumb, dynamic hint) =>
null;
}