Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
902dd1d
add options.attachStacktrace
rxlabz Nov 10, 2020
e1f347d
fix case
rxlabz Nov 10, 2020
07048d5
add a _exceptionFactory.stacktraceFactory getter
rxlabz Nov 10, 2020
a61f760
- auto-attach stacktrace to exception only if options.attachStackTrace
rxlabz Nov 10, 2020
0a5addb
fix test
rxlabz Nov 10, 2020
f7f2458
add client._stackTraceFactory
rxlabz Nov 10, 2020
a526771
fix import
rxlabz Nov 10, 2020
3c6cd02
attach stacktrace only if frames is not empty
rxlabz Nov 10, 2020
65c4d2b
attachStackTrace doc
rxlabz Nov 10, 2020
6d349fc
attachStackTrace only if event.stacktrace is null
rxlabz Nov 10, 2020
23738c2
init factories in client.ctor
rxlabz Nov 10, 2020
cbb7be8
feedback
rxlabz Nov 10, 2020
ee39da9
required SentryExceptionFactory.stacktraceFactory
rxlabz Nov 10, 2020
d901e26
type SentryEvent.stackTrace as SentryStackTrace
rxlabz Nov 11, 2020
490d3b7
serialize exception or stacktrace
rxlabz Nov 11, 2020
75d69a6
feedback
rxlabz Nov 11, 2020
b0c4a8e
feedbacks
rxlabz Nov 11, 2020
a027fe8
attach stacktrace only if stacktrace is null
rxlabz Nov 11, 2020
b92f661
event.stacktrace only if event.throwable AND event.exception are null
rxlabz Nov 11, 2020
7f4d18e
remove null conditional instanciation in exceptionFactory
rxlabz Nov 11, 2020
b3f546d
attach the passed stacktrace if attachStackTrace is true
rxlabz Nov 11, 2020
67c9ae8
fix & refactor client._prepareEvent
rxlabz Nov 11, 2020
c65800e
add stacktrace and exception.stacktrace tests
rxlabz Nov 11, 2020
ac1ee38
feedbacks
rxlabz Nov 11, 2020
3077703
fix frame.uri.pathSegments.isNotEmpty
rxlabz Nov 12, 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 @@ -12,6 +12,7 @@
- feat: add missing protocol classes
- fix: logger method and refactoring little things
- fix: sentry protocol is v7
- feat: add an attachStackTrace options

## 4.0.0-alpha.1

Expand Down
9 changes: 9 additions & 0 deletions dart/lib/src/protocol/sentry_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,15 @@ class SentryEvent {
json['exception'] = {
'values': [exception.toJson()].toList(growable: false)
};
} else if (stackTrace is SentryStackTrace) {
json['threads'] = {
'values': [
{
'id': 0,
'stacktrace': (stackTrace as SentryStackTrace).toJson(),
}
]
};
}

if (level != null) {
Expand Down
46 changes: 34 additions & 12 deletions dart/lib/src/sentry_client.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'dart:async';
import 'dart:math';

import 'package:meta/meta.dart';
import 'package:sentry/src/sentry_stack_trace_factory.dart';

import 'protocol.dart';
import 'scope.dart';
import 'sentry_exception_factory.dart';
Expand All @@ -11,6 +14,16 @@ import 'version.dart';

/// Logs crash reports and events to the Sentry.io service.
class SentryClient {
final SentryOptions _options;

final Random _random;

final SentryExceptionFactory _exceptionFactory;

final SentryStackTraceFactory _stackTraceFactory;

static final _sentryId = Future.value(SentryId.empty());

/// Instantiates a client using [SentryOptions]
factory SentryClient(SentryOptions options) {
if (options == null) {
Expand All @@ -20,21 +33,22 @@ class SentryClient {
if (options.transport is NoOpTransport) {
options.transport = HttpTransport(options);
}
return SentryClient._(options);
}

final SentryOptions _options;

final Random _random;

final SentryExceptionFactory _exceptionFactory;

static final _sentryId = Future.value(SentryId.empty());
return SentryClient._(
options,
stackTraceFactory: SentryStackTraceFactory(options),
);
}

/// Instantiates a client using [SentryOptions]
SentryClient._(this._options, {SentryExceptionFactory exceptionFactory})
: _exceptionFactory =
exceptionFactory ?? SentryExceptionFactory(options: _options),
SentryClient._(
this._options, {
@required SentryStackTraceFactory stackTraceFactory,
}) : _stackTraceFactory = stackTraceFactory,
_exceptionFactory = SentryExceptionFactory(
options: _options,
stacktraceFactory: stackTraceFactory,
),
_random = _options.sampleRate == null ? null : Random();

/// Reports an [event] to Sentry.io.
Expand Down Expand Up @@ -99,6 +113,14 @@ class SentryClient {
event = event.copyWith(exception: sentryException);
}

if (_options.attachStackTrace &&
event.throwable == null &&
event.exception == null) {
final frames = _stackTraceFactory.getStackFrames(StackTrace.current);

event = event.copyWith(stackTrace: SentryStackTrace(frames: frames));
}

return event;
}

Expand Down
21 changes: 14 additions & 7 deletions dart/lib/src/sentry_exception_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import 'sentry_stack_trace_factory.dart';

/// class to convert Dart Error and exception to SentryException
class SentryExceptionFactory {
final SentryOptions _options;

SentryStackTraceFactory _stacktraceFactory;

SentryStackTraceFactory get stacktraceFactory => _stacktraceFactory;

SentryExceptionFactory({
SentryStackTraceFactory stacktraceFactory,
@required SentryOptions options,
}) {
if (options == null) {
}) : _options = options {
if (_options == null) {
throw ArgumentError('SentryOptions is required.');
}

_stacktraceFactory = stacktraceFactory ?? SentryStackTraceFactory(options);
_stacktraceFactory = stacktraceFactory ?? SentryStackTraceFactory(_options);
}

SentryException getSentryException(
Expand All @@ -26,13 +30,16 @@ class SentryExceptionFactory {
}) {
if (exception is Error) {
stackTrace ??= exception.stackTrace;
} else {
} else if (_options.attachStackTrace) {
stackTrace ??= StackTrace.current;
}

final sentryStackTrace = SentryStackTrace(
frames: _stacktraceFactory.getStackFrames(stackTrace),
);
SentryStackTrace sentryStackTrace;
if (stackTrace != null) {
sentryStackTrace = SentryStackTrace(
frames: _stacktraceFactory.getStackFrames(stackTrace),
);
}

final sentryException = SentryException(
type: '${exception.runtimeType}',
Expand Down
8 changes: 8 additions & 0 deletions dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ class SentryOptions {
_sdk = sdk ?? _sdk;
}

bool _attachStackTrace = true;

bool get attachStackTrace => _attachStackTrace;

set attachStackTrace(bool attachStacktrace) {
_attachStackTrace = attachStacktrace ?? _attachStackTrace;
}

// TODO: Scope observers, enableScopeSync

// TODO: sendDefaultPii
Expand Down
10 changes: 6 additions & 4 deletions dart/lib/src/sentry_stack_trace_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ class SentryStackTraceFactory {

final frames = <SentryStackFrame>[];
for (var t = 0; t < chain.traces.length; t += 1) {
final encodedFrames =
chain.traces[t].frames.map((f) => encodeStackTraceFrame(f));
final encodedFrames = chain.traces[t].frames
// we don't want to add our own frames
.where((frame) => frame.package != 'sentry')
.map((f) => encodeStackTraceFrame(f));

frames.addAll(encodedFrames);

Expand Down Expand Up @@ -101,14 +103,14 @@ class SentryStackTraceFactory {

if (_inAppIncludes != null) {
for (final include in _inAppIncludes) {
if (frame.package != null && frame.package.startsWith(include)) {
if (frame.package != null && frame.package == include) {
return true;
}
}
}
if (_inAppExcludes != null) {
for (final exclude in _inAppExcludes) {
if (frame.package != null && frame.package.startsWith(exclude)) {
if (frame.package != null && frame.package == exclude) {
return false;
}
}
Expand Down
75 changes: 75 additions & 0 deletions dart/test/sentry_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ void main() {
expect(capturedEvent.message.formatted, 'simple message 1');
expect(capturedEvent.message.template, 'simple message %d');
expect(capturedEvent.message.params, [1]);

expect(capturedEvent.stackTrace is SentryStackTrace, true);
});

test('should capture message without stacktrace', () async {
final client = SentryClient(options..attachStackTrace = false);
await client.captureMessage('message', level: SentryLevel.error);

final capturedEvent = (verify(
options.transport.send(captureAny),
).captured.first) as SentryEvent;

expect(capturedEvent.stackTrace, isNull);
});
});

Expand Down Expand Up @@ -141,6 +154,68 @@ void main() {
expect(capturedEvent.exception.stacktrace.frames.first.lineNo, 46);
expect(capturedEvent.exception.stacktrace.frames.first.colNo, 9);
});

test('should capture exception with Stackframe.current', () async {
try {
throw Exception('Error');
} catch (err) {
exception = err;
}

final client = SentryClient(options);
await client.captureException(exception);

final capturedEvent = (verify(
options.transport.send(captureAny),
).captured.first) as SentryEvent;

expect(capturedEvent.exception.stacktrace, isNotNull);
});

test('should capture exception without Stackframe.current', () async {
try {
throw Exception('Error');
} catch (err) {
exception = err;
}

final client = SentryClient(options..attachStackTrace = false);
await client.captureException(exception);

final capturedEvent = (verify(
options.transport.send(captureAny),
).captured.first) as SentryEvent;

expect(capturedEvent.exception.stacktrace, isNull);
});

test('should not capture sentry frames exception', () async {
try {
throw Exception('Error');
} catch (err) {
exception = err;
}

final stacktrace = '''
#0 init (package:sentry/sentry.dart:46:9)
#1 bar (file:///pathto/test.dart:46:9)
<asynchronous suspension>
#2 capture (package:sentry/sentry.dart:46:9)
''';

final client = SentryClient(options);
await client.captureException(exception, stackTrace: stacktrace);

final capturedEvent = (verify(
options.transport.send(captureAny),
).captured.first) as SentryEvent;

expect(
capturedEvent.exception.stacktrace.frames
.every((frame) => frame.package != 'sentry'),
true,
);
});
});

group('SentryClient : apply scope to the captured event', () {
Expand Down
23 changes: 23 additions & 0 deletions dart/test/sentry_event_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,29 @@ void main() {
expect(serialized['exception'], null);
});

test('should serialize stacktrace if SentryStacktrace', () {
final stacktrace =
SentryStackTrace(frames: [SentryStackFrame(function: 'main')]);
final serialized = SentryEvent(stackTrace: stacktrace).toJson();
expect(serialized['threads']['values'].first['stacktrace'], isNotNull);
});

test('should not serialize event.stacktrace if event.exception is set', () {
final stacktrace =
SentryStackTrace(frames: [SentryStackFrame(function: 'main')]);
final serialized = SentryEvent(
exception: SentryException(value: 'Bad state', type: 'StateError'),
stackTrace: stacktrace,
).toJson();
expect(serialized['stacktrace'], isNull);
});

test('should not serialize stacktrace if not SentryStacktrace', () {
final stacktrace = '#0 baz (file:///pathto/test.dart:50:3)';
final serialized = SentryEvent(stackTrace: stacktrace).toJson();
expect(serialized['stacktrace'], isNull);
});

test('serializes to JSON with sentryException', () {
var sentryException;
try {
Expand Down