Skip to content

Report isolate as thread #847

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 23 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d00520d
isolate as thread
ueman Apr 22, 2022
61d0054
improve code
ueman Apr 22, 2022
173d9b3
run test only on dart io
ueman Apr 25, 2022
b3233b5
add changelog
ueman Apr 25, 2022
3c85863
Merge branch 'main' into feature/isolate-as-thread
ueman Apr 28, 2022
e877647
fix changelog
ueman Apr 28, 2022
1f6b5f9
thread is crashy
ueman Apr 28, 2022
bdb14c5
Merge branch 'main' into feature/isolate-as-thread
ueman May 3, 2022
8a41a91
Merge branch 'main' into feature/isolate-as-thread
marandaneto May 5, 2022
aeed66d
only add thread if enabled
ueman May 5, 2022
757ef06
fix regression
ueman May 5, 2022
e81529b
Update dart/lib/src/sentry_options.dart
ueman May 9, 2022
70c13d0
Merge branch 'main' into feature/isolate-as-thread
ueman May 9, 2022
5ea059b
Fix changelog
ueman May 10, 2022
b864304
Merge branch 'main' into feature/isolate-as-thread
ueman May 10, 2022
541e6e7
Merge branch 'main' into feature/isolate-as-thread
ueman May 10, 2022
4faa354
Merge branch 'main' into feature/isolate-as-thread
ueman May 13, 2022
a09bf9c
Merge branch 'main' into feature/isolate-as-thread
marandaneto May 13, 2022
40072f4
Merge branch 'feature/isolate-as-thread' of github.com:ueman/sentry-d…
marandaneto May 13, 2022
5187f64
Merge branch 'main' into feature/isolate-as-thread
ueman May 19, 2022
20c3e2d
Merge branch 'main' into feature/isolate-as-thread
ueman May 25, 2022
2153aae
enable attachThreads in example
ueman May 25, 2022
32fc922
Merge branch 'main' into feature/isolate-as-thread
ueman Jun 7, 2022
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 @@ -2,6 +2,7 @@

## 6.6.0-alpha.2

- Attach Isolate name to thread context (#847)
- Fix serialization of threads (#844)
- Feat: Allow manual init of the Native SDK (#765)

Expand Down
47 changes: 40 additions & 7 deletions dart/lib/src/sentry_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'sentry_options.dart';
import 'sentry_stack_trace_factory.dart';
import 'transport/http_transport.dart';
import 'transport/noop_transport.dart';
import 'utils/isolate_utils.dart';
import 'version.dart';
import 'sentry_envelope.dart';
import 'client_reports/client_report_recorder.dart';
Expand Down Expand Up @@ -140,18 +141,48 @@ class SentryClient {
return event;
}

if (event.exceptions?.isNotEmpty ?? false) return event;
if (event.exceptions?.isNotEmpty ?? false) {
return event;
}

final isolateName = getIsolateName();
// Isolates have no id, so the hashCode of the name will be used as id
final isolateId = isolateName?.hashCode;

if (event.throwableMechanism != null) {
final sentryException = _exceptionFactory.getSentryException(
var sentryException = _exceptionFactory.getSentryException(
event.throwableMechanism,
stackTrace: stackTrace,
);

return event.copyWith(exceptions: [
...(event.exceptions ?? []),
sentryException,
]);
if (_options.platformChecker.isWeb) {
return event.copyWith(
exceptions: [
...?event.exceptions,
sentryException,
],
);
}

SentryThread? thread;

if (isolateName != null && _options.attachThreads) {
sentryException = sentryException.copyWith(threadId: isolateId);
thread = SentryThread(
id: isolateId,
name: isolateName,
crashed: true,
current: true,
);
}

return event.copyWith(
exceptions: [...?event.exceptions, sentryException],
threads: [
...?event.threads,
if (thread != null) thread,
],
);
}

// The stacktrace is not part of an exception,
Expand All @@ -163,8 +194,10 @@ class SentryClient {

if (frames.isNotEmpty) {
event = event.copyWith(threads: [
...(event.threads ?? []),
...?event.threads,
SentryThread(
name: isolateName,
id: isolateId,
crashed: false,
current: true,
stacktrace: SentryStackTrace(frames: frames),
Expand Down
8 changes: 3 additions & 5 deletions dart/lib/src/sentry_exception_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,11 @@ class SentryExceptionFactory {

// if --obfuscate feature is enabled, 'type' won't be human readable.
// https://flutter.dev/docs/deployment/obfuscate#caveat
final sentryException = SentryException(
type: '${throwable.runtimeType}',
value: '$throwable',
return SentryException(
type: (throwable.runtimeType).toString(),
value: throwable.toString(),
mechanism: mechanism,
stackTrace: sentryStackTrace,
);

return sentryException;
}
}
10 changes: 9 additions & 1 deletion dart/lib/src/sentry_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,15 @@ class SentryOptions {
/// variables. This is useful in tests.
EnvironmentVariables environmentVariables = EnvironmentVariables.instance();

/// When enabled, all the threads are automatically attached to all logged events (Android).
/// When enabled, the current isolate will be attached to the event.
/// This only applies to Dart:io platforms and only the current isolate.
/// The Dart runtime doesn't provide information about other active isolates.
///
/// When running on web, this option has no effect at all.
///
/// When running in the Flutter context, this enables attaching of threads
/// for native events, if supported for the native platform.
/// Currently, this is only supported on Android.
bool attachThreads = false;

/// Whether to send personal identifiable information along with events
Expand Down
3 changes: 3 additions & 0 deletions dart/lib/src/utils/_io_get_isolate_name.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import 'dart:isolate';

String? getIsolateName() => Isolate.current.debugName;
1 change: 1 addition & 0 deletions dart/lib/src/utils/_web_get_isolate_name.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
String? getIsolateName() => null;
4 changes: 4 additions & 0 deletions dart/lib/src/utils/isolate_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import '_io_get_isolate_name.dart'
if (dart.library.html) '_web_get_isolate_name.dart' as isolate_getter;

String? getIsolateName() => isolate_getter.getIsolateName();
49 changes: 48 additions & 1 deletion dart/test/sentry_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,50 @@ void main() {
expect(capturedEvent.exceptions?.first.stackTrace, isNotNull);
});

test(
'should attach isolate info in thread',
() async {
final client = fixture.getSut(attachThreads: true);

await client.captureException(
Exception(),
stackTrace: StackTrace.current,
);

final capturedEnvelope = (fixture.transport).envelopes.first;
final capturedEvent = await eventFromEnvelope(capturedEnvelope);

expect(capturedEvent.threads?.first.current, true);
expect(capturedEvent.threads?.first.crashed, true);
expect(capturedEvent.threads?.first.name, isNotNull);
expect(capturedEvent.threads?.first.id, isNotNull);

expect(
capturedEvent.exceptions?.first.threadId,
capturedEvent.threads?.first.id,
);
},
onPlatform: {'js': Skip("Isolates don't exist on the web")},
);

test(
'should not attach isolate info in thread if disabled',
() async {
final client = fixture.getSut(attachThreads: false);

await client.captureException(
Exception(),
stackTrace: StackTrace.current,
);

final capturedEnvelope = (fixture.transport).envelopes.first;
final capturedEvent = await eventFromEnvelope(capturedEnvelope);

expect(capturedEvent.threads, null);
},
onPlatform: {'js': Skip("Isolates don't exist on the web")},
);

test('should capture message', () async {
final client = fixture.getSut();
await client.captureMessage(
Expand Down Expand Up @@ -1014,6 +1058,7 @@ class Fixture {
SentryClient getSut({
bool sendDefaultPii = false,
bool attachStacktrace = true,
bool attachThreads = false,
double? sampleRate,
BeforeSendCallback? beforeSend,
EventProcessor? eventProcessor,
Expand All @@ -1029,14 +1074,16 @@ class Fixture {
options.tracesSampleRate = 1.0;
options.sendDefaultPii = sendDefaultPii;
options.attachStacktrace = attachStacktrace;
options.attachThreads = attachThreads;
options.sampleRate = sampleRate;
options.beforeSend = beforeSend;

if (eventProcessor != null) {
options.addEventProcessor(eventProcessor);
}
options.transport = transport;
final client = SentryClient(options);
// hub.bindClient(client);

if (provideMockRecorder) {
options.recorder = recorder;
}
Expand Down