Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@

## Unreleased

### Features

- Improve `SentryException#value`, remove stringified stack trace ([##1470](https://github.com/getsentry/sentry-dart/pull/#1470))

### Dependencies

- Bump Android SDK from v6.18.1 to v6.19.0 ([#1455](https://github.com/getsentry/sentry-dart/pull/1455))
Expand Down
5 changes: 4 additions & 1 deletion dart/lib/src/sentry_exception_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'protocol.dart';
import 'sentry_options.dart';
import 'sentry_stack_trace_factory.dart';
import 'throwable_mechanism.dart';
import 'utils/stack_trace_utils.dart';

/// class to convert Dart Error and exception to SentryException
class SentryExceptionFactory {
Expand Down Expand Up @@ -57,11 +58,13 @@ class SentryExceptionFactory {
}
}

final throwableString = throwable.toString();

// if --obfuscate feature is enabled, 'type' won't be human readable.
// https://flutter.dev/docs/deployment/obfuscate#caveat
return SentryException(
type: (throwable.runtimeType).toString(),
value: throwable.toString(),
value: throwableString.isStackTrace() ? null : throwableString,
mechanism: mechanism,
stackTrace: sentryStackTrace,
throwable: throwable,
Expand Down
25 changes: 25 additions & 0 deletions dart/lib/src/utils/stack_trace_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:meta/meta.dart';

@internal
extension StackTraceUtils on String {
bool isStackTrace() {
final frameNumberPrefix = RegExp(r'^#\d+', multiLine: true);
final matchesFrameNumber = frameNumberPrefix.hasMatch(this);

final fileAndLineNumberSuffix =
RegExp(r'.dart:\d+:\d+\)$', multiLine: true);
final matchesFileAndLineNumber = fileAndLineNumberSuffix.hasMatch(this);

final abs = RegExp(r'\s(abs)\s');
final virt = RegExp(r'\s(virt)\s');
final matchesAbsAndVirt = abs.hasMatch(this) & virt.hasMatch(this);

final hexSuffix = RegExp(r'\+0(x)\w+$', multiLine: true);
final matchesHexSuffix = hexSuffix.hasMatch(this);

final isStackTrace = matchesFrameNumber & matchesFileAndLineNumber;
final isObfuscatedStackTrace =
matchesFrameNumber & (matchesAbsAndVirt || matchesHexSuffix);
return isStackTrace || isObfuscatedStackTrace;
}
}
18 changes: 18 additions & 0 deletions dart/test/sentry_exception_factory_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ void main() {

expect(sentryException.throwable, throwable);
});

test('should not assigns stackTrace string to value', () {
final stackTraceError = StackTraceError();
final sentryException =
fixture.getSut().getSentryException(stackTraceError);
expect(sentryException.value, isNull);
});
}

class CustomError extends Error {}
Expand All @@ -187,6 +194,17 @@ class CustomExceptionStackTraceExtractor
}
}

class StackTraceError extends Error {
@override
String toString() {
return '''
#0 baz (file:///pathto/test.dart:50:3)
<asynchronous suspension>
#1 bar (file:///pathto/test.dart:46:9)
''';
}
}

class Fixture {
final options = SentryOptions(dsn: fakeDsn);

Expand Down
74 changes: 74 additions & 0 deletions dart/test/utils/stack_trace_utils_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'package:sentry/src/utils/stack_trace_utils.dart';
import 'package:test/test.dart';

void main() {
final dartStackTrace = '''
randomPrefix
#0 main (file:///Users/denis/Repos/other/dart-stacktrace/main.dart:2:20)
#1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:296:19)
#2 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:189:12)
randomSuffix
''';

final flutterStackTrace = '''
randomPrefix
flutter: #0 MainScaffold.build.<anonymous closure> (package:sentry_flutter_example/main.dart:142:47)
#1 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1154:21)
#2 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:275:24)
#3 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:654:11)
#4 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:311:5)
#5 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:281:7)
#6 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:167:27)
#7 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:469:20)
#8 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:445:22)
#9 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:331:11)
#10 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:400:7)<…>
randomSuffix
''';

final flutterObfuscatedStackTrace = '''
#00 abs 00000075266c2fbf virt 0000000000697fbf _kDartIsolateSnapshotInstructions+0x37e63f
#1 abs 000000752685211f virt 000000000082711f _kDartIsolateSnapshotInstructions+0x50d79f
#2 abs 0000007526851cb3 virt 0000000000826cb3 _kDartIsolateSnapshotInstructions+0x50d333
#3 abs 0000007526851c63 virt 0000000000826c63 _kDartIsolateSnapshotInstructions+0x50d2e3
#4 abs 0000007526851bf3 virt 0000000000826bf3 _kDartIsolateSnapshotInstructions+0x50d273
''';

test('dart stack trace detected', () {
expect(dartStackTrace.isStackTrace(), isTrue);
});

test('flutter stack trace detected', () {
expect(flutterStackTrace.isStackTrace(), isTrue);
});

test('flutter obfuscated stack trace detected', () {
expect(flutterObfuscatedStackTrace.isStackTrace(), isTrue);
});

test('stack trace not detected with frame number only', () {
expect('#0'.isStackTrace(), isFalse);
});

test('stack trace not detected with file name and line number only', () {
expect('foo.dart:9000:1)'.isStackTrace(), isFalse);
});

test('stack trace not detected abs only', () {
expect(' abs '.isStackTrace(), isFalse);
});

test('stack trace not detected virt only', () {
expect(' virt '.isStackTrace(), isFalse);
});

test('stack trace not detected abs & virt only', () {
expect(
' abs 00000075266c2fbf virt 0000000000697fbf'.isStackTrace(), isFalse);
});

test('stack trace not detected hex suffix only', () {
expect(
'_kDartIsolateSnapshotInstructions+0x50d273'.isStackTrace(), isFalse);
});
}
8 changes: 8 additions & 0 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ class MainScaffold extends StatelessWidget {
child: Column(
children: [
const Center(child: Text('Trigger an action:\n')),
ElevatedButton(
onPressed: () {
final stackTrace = StackTrace.current;
NetworkError(type: NetworkErrorType.unknown, error: Instance of 'iH')
print(stackTrace);
},
child: const Text('StackTrace'),
),
ElevatedButton(
onPressed: () => sqfliteTest(),
child: const Text('sqflite'),
Expand Down