Skip to content

Commit b2be39c

Browse files
authored
Merge branch 'main' into fix/twp-http-clients
2 parents fa47361 + 2ed9d5f commit b2be39c

20 files changed

Lines changed: 840 additions & 159 deletions

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
steps:
2020
- name: Get auth token
2121
id: token
22-
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0
22+
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
2323
with:
2424
app-id: ${{ vars.SENTRY_RELEASE_BOT_CLIENT_ID }}
2525
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}

CHANGELOG.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,22 @@ Sentry.addFeatureFlag('my-feature', true);
1616

1717
### Behavioral changes
1818

19+
- Mutable Data Classes ([#2818](https://github.com/getsentry/sentry-dart/pull/2818))
20+
- Some SDK classes do not have `const` constructors anymore.
21+
- The `copyWith` and `clone` methods of SDK classes were deprecated.
1922
- Set log level to `warning` by default when `debug = true` ([#2836](https://github.com/getsentry/sentry-dart/pull/2836))
23+
- Parent-child relationship for the PlatformExceptions and Cause ([#2803](https://github.com/getsentry/sentry-dart/pull/2803))
24+
- Improves and changes exception grouping
2025

2126
### API Changes
2227

2328
- Update naming of `LoadImagesListIntegration` to `LoadNativeDebugImagesIntegration` ([#2833](https://github.com/getsentry/sentry-dart/pull/2833))
2429

2530
### Dependencies
2631

27-
- Bump Android SDK from v8.2.0 to v8.6.0 ([#2819](https://github.com/getsentry/sentry-dart/pull/2819), [#2831](https://github.com/getsentry/sentry-dart/pull/2831))
28-
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#860)
29-
- [diff](https://github.com/getsentry/sentry-java/compare/8.2.0...8.6.0)
32+
- Bump Android SDK from v8.2.0 to v8.7.0 ([#2819](https://github.com/getsentry/sentry-dart/pull/2819), [#2831](https://github.com/getsentry/sentry-dart/pull/2831), [#2848](https://github.com/getsentry/sentry-dart/pull/2848))
33+
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#870)
34+
- [diff](https://github.com/getsentry/sentry-java/compare/8.2.0...8.7.0)
3035
- Bump Cocoa SDK from v8.46.0 to v8.48.0 ([#2820](https://github.com/getsentry/sentry-dart/pull/2820))
3136
- [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8480)
3237
- [diff](https://github.com/getsentry/sentry-cocoa/compare/8.46.0...8.48.0)
@@ -37,12 +42,6 @@ Sentry.addFeatureFlag('my-feature', true);
3742
- [changelog](https://github.com/dart-lang/native/blob/main/pkgs/jni/CHANGELOG.md#0141)
3843
- [diff](https://github.com/dart-lang/native/compare/jnigen-v0.14.0..jnigen-v0.14.1)
3944

40-
### Behavioral changes
41-
42-
- Mutable Data Classes ([#2818](https://github.com/getsentry/sentry-dart/pull/2818))
43-
- Some SDK classes do not have `const` constructors anymore.
44-
- The `copyWith` and `clone` methods of SDK classes were deprecated.
45-
4645
## 9.0.0-alpha.2
4746

4847
### Features
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import '../../event_processor.dart';
2+
import '../../protocol.dart';
3+
import '../../hint.dart';
4+
5+
/// Group exceptions into a flat list with references to hierarchy.
6+
class ExceptionGroupEventProcessor implements EventProcessor {
7+
@override
8+
SentryEvent? apply(SentryEvent event, Hint hint) {
9+
final sentryExceptions = event.exceptions ?? [];
10+
if (sentryExceptions.isEmpty) {
11+
return event;
12+
}
13+
final firstException = sentryExceptions.first;
14+
15+
if (sentryExceptions.length > 1 || firstException.exceptions == null) {
16+
// If already a list or no child exceptions, no grouping possible/needed.
17+
return event;
18+
} else {
19+
event.exceptions =
20+
firstException.flatten().reversed.toList(growable: false);
21+
return event;
22+
}
23+
}
24+
}
25+
26+
extension _SentryExceptionFlatten on SentryException {
27+
List<SentryException> flatten({int? parentId, int id = 0}) {
28+
final exceptions = this.exceptions ?? [];
29+
30+
final newMechanism = mechanism ?? Mechanism(type: "generic");
31+
newMechanism
32+
..type = id > 0 ? "chained" : newMechanism.type
33+
..parentId = parentId
34+
..exceptionId = id
35+
..isExceptionGroup = exceptions.isNotEmpty ? true : null;
36+
37+
mechanism = newMechanism;
38+
39+
var all = <SentryException>[];
40+
all.add(this);
41+
42+
if (exceptions.isNotEmpty) {
43+
final parentId = id;
44+
for (var exception in exceptions) {
45+
id++;
46+
final flattenedExceptions =
47+
exception.flatten(parentId: parentId, id: id);
48+
id = flattenedExceptions.lastOrNull?.mechanism?.exceptionId ?? id;
49+
all.addAll(flattenedExceptions);
50+
}
51+
}
52+
return all.toList(growable: false);
53+
}
54+
}

dart/lib/src/event_processor/exception/io_exception_event_processor.dart

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,27 @@ class IoExceptionEventProcessor implements ExceptionEventProcessor {
4343
SocketException exception,
4444
SentryEvent event,
4545
) {
46-
final address = exception.address;
4746
final osError = exception.osError;
47+
SentryException? osException;
48+
List<SentryException>? exceptions = event.exceptions;
49+
if (osError != null) {
50+
// OSError is the underlying error
51+
// https://api.dart.dev/stable/dart-io/SocketException/osError.html
52+
// https://api.dart.dev/stable/dart-io/OSError-class.html
53+
osException = _sentryExceptionFromOsError(osError);
54+
final exception = event.exceptions?.firstOrNull;
55+
if (exception != null) {
56+
exception.addException(osException);
57+
} else {
58+
exceptions = [osException];
59+
}
60+
} else {
61+
exceptions = event.exceptions;
62+
}
63+
64+
final address = exception.address;
4865
if (address == null) {
49-
event.exceptions = [
50-
// OSError is the underlying error
51-
// https://api.dart.dev/stable/dart-io/SocketException/osError.html
52-
// https://api.dart.dev/stable/dart-io/OSError-class.html
53-
if (osError != null) _sentryExceptionfromOsError(osError),
54-
...?event.exceptions,
55-
];
66+
event.exceptions = exceptions;
5667
return event;
5768
}
5869
SentryRequest? request;
@@ -71,15 +82,9 @@ class IoExceptionEventProcessor implements ExceptionEventProcessor {
7182
}
7283
}
7384

74-
event.request = event.request ?? request;
75-
event.exceptions = [
76-
// OSError is the underlying error
77-
// https://api.dart.dev/stable/dart-io/SocketException/osError.html
78-
// https://api.dart.dev/stable/dart-io/OSError-class.html
79-
if (osError != null) _sentryExceptionfromOsError(osError),
80-
...?event.exceptions,
81-
];
82-
return event;
85+
return event
86+
..request = event.request ?? request
87+
..exceptions = exceptions;
8388
}
8489

8590
// https://api.dart.dev/stable/dart-io/FileSystemException-class.html
@@ -88,18 +93,24 @@ class IoExceptionEventProcessor implements ExceptionEventProcessor {
8893
SentryEvent event,
8994
) {
9095
final osError = exception.osError;
91-
event.exceptions = [
96+
97+
if (osError != null) {
9298
// OSError is the underlying error
93-
// https://api.dart.dev/stable/dart-io/FileSystemException/osError.html
99+
// https://api.dart.dev/stable/dart-io/SocketException/osError.html
94100
// https://api.dart.dev/stable/dart-io/OSError-class.html
95-
if (osError != null) _sentryExceptionfromOsError(osError),
96-
...?event.exceptions,
97-
];
101+
final osException = _sentryExceptionFromOsError(osError);
102+
final exception = event.exceptions?.firstOrNull;
103+
if (exception != null) {
104+
exception.addException(osException);
105+
} else {
106+
event.exceptions = [osException];
107+
}
108+
}
98109
return event;
99110
}
100111
}
101112

102-
SentryException _sentryExceptionfromOsError(OSError osError) {
113+
SentryException _sentryExceptionFromOsError(OSError osError) {
103114
return SentryException(
104115
type: osError.runtimeType.toString(),
105116
value: osError.toString(),
@@ -110,6 +121,7 @@ SentryException _sentryExceptionfromOsError(OSError osError) {
110121
meta: {
111122
'errno': {'number': osError.errorCode},
112123
},
124+
source: 'osError',
113125
),
114126
);
115127
}

dart/lib/src/exception_cause.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/// Holds inner exception and stackTrace combinations contained in other exceptions
22
class ExceptionCause {
3-
ExceptionCause(this.exception, this.stackTrace);
3+
ExceptionCause(this.exception, this.stackTrace, {this.source});
44

55
dynamic exception;
66
dynamic stackTrace;
7+
String? source;
78
}

dart/lib/src/protocol/sentry_exception.dart

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ class SentryException {
2626
dynamic throwable;
2727

2828
@internal
29-
final Map<String, dynamic>? unknown;
29+
Map<String, dynamic>? unknown;
30+
31+
List<SentryException>? _exceptions;
3032

3133
SentryException({
3234
required this.type,
@@ -86,10 +88,25 @@ class SentryException {
8688
type: type ?? this.type,
8789
value: value ?? this.value,
8890
module: module ?? this.module,
89-
stackTrace: stackTrace ?? this.stackTrace,
90-
mechanism: mechanism ?? this.mechanism,
91+
stackTrace: stackTrace ?? this.stackTrace?.copyWith(),
92+
mechanism: mechanism ?? this.mechanism?.copyWith(),
9193
threadId: threadId ?? this.threadId,
9294
throwable: throwable ?? this.throwable,
9395
unknown: unknown,
9496
);
97+
98+
@internal
99+
List<SentryException>? get exceptions =>
100+
_exceptions != null ? List.unmodifiable(_exceptions!) : null;
101+
102+
@internal
103+
set exceptions(List<SentryException>? value) {
104+
_exceptions = value;
105+
}
106+
107+
@internal
108+
void addException(SentryException exception) {
109+
_exceptions ??= [];
110+
_exceptions!.add(exception);
111+
}
95112
}

dart/lib/src/recursive_exception_cause_extractor.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ class RecursiveExceptionCauseExtractor {
1616
final circularityDetector = <dynamic>{};
1717

1818
var currentException = exception;
19-
ExceptionCause? currentExceptionCause =
20-
ExceptionCause(exception, stackTrace);
19+
ExceptionCause? currentExceptionCause = ExceptionCause(
20+
exception,
21+
stackTrace,
22+
);
2123

2224
while (currentException != null &&
2325
currentExceptionCause != null &&

dart/lib/src/sentry.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'environment/environment_variables.dart';
77
import 'event_processor/deduplication_event_processor.dart';
88
import 'event_processor/enricher/enricher_event_processor.dart';
99
import 'event_processor/exception/exception_event_processor.dart';
10+
import 'event_processor/exception/exception_group_event_processor.dart';
1011
import 'hint.dart';
1112
import 'hub.dart';
1213
import 'hub_adapter.dart';
@@ -113,6 +114,9 @@ class Sentry {
113114
options.addEventProcessor(DeduplicationEventProcessor(options));
114115

115116
options.prependExceptionTypeIdentifier(DartExceptionTypeIdentifier());
117+
118+
// Added last to ensure all error events have correct parent/child relationships
119+
options.addEventProcessor(ExceptionGroupEventProcessor());
116120
}
117121

118122
/// This method reads available environment variables and uses them
@@ -360,7 +364,11 @@ class Sentry {
360364
/// Gets the current active transaction or span bound to the scope.
361365
static ISentrySpan? getSpan() => _hub.getSpan();
362366

363-
static Future<void> addFeatureFlag(String name, bool value) async {
367+
static Future<void> addFeatureFlag(String name, dynamic value) async {
368+
if (value is! bool) {
369+
return;
370+
}
371+
364372
final featureFlagsIntegration = currentHub.options.integrations
365373
.whereType<FeatureFlagsIntegration>()
366374
.firstOrNull;

dart/lib/src/sentry_client.dart

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,13 @@ class SentryClient {
211211

212212
SentryEvent _prepareEvent(SentryEvent event, Hint hint,
213213
{dynamic stackTrace}) {
214-
event.serverName = event.serverName ?? _options.serverName;
215-
event.dist = event.dist ?? _options.dist;
216-
event.environment = event.environment ?? _options.environment;
217-
event.release = event.release ?? _options.release;
218-
event.sdk = event.sdk ?? _options.sdk;
219-
event.platform = event.platform ?? sdkPlatform(_options.platform.isWeb);
214+
event
215+
..serverName = event.serverName ?? _options.serverName
216+
..dist = event.dist ?? _options.dist
217+
..environment = event.environment ?? _options.environment
218+
..release = event.release ?? _options.release
219+
..sdk = event.sdk ?? _options.sdk
220+
..platform = event.platform ?? sdkPlatform(_options.platform.isWeb);
220221

221222
if (event is SentryTransaction) {
222223
return event;
@@ -235,18 +236,26 @@ class SentryClient {
235236
final isolateId = isolateName?.hashCode;
236237

237238
if (event.throwableMechanism != null) {
238-
final extractedExceptions = _exceptionFactory.extractor
239+
final extractedExceptionCauses = _exceptionFactory.extractor
239240
.flatten(event.throwableMechanism, stackTrace);
240241

241-
final sentryExceptions = <SentryException>[];
242+
SentryException? rootException;
243+
SentryException? currentException;
242244
final sentryThreads = <SentryThread>[];
243245

244-
for (final extractedException in extractedExceptions) {
246+
for (final extractedExceptionCause in extractedExceptionCauses) {
245247
var sentryException = _exceptionFactory.getSentryException(
246-
extractedException.exception,
247-
stackTrace: extractedException.stackTrace,
248+
extractedExceptionCause.exception,
249+
stackTrace: extractedExceptionCause.stackTrace,
248250
removeSentryFrames: hint.get(TypeCheckHint.currentStackTrace),
249251
);
252+
if (extractedExceptionCause.source != null) {
253+
var mechanism =
254+
sentryException.mechanism ?? Mechanism(type: "generic");
255+
256+
mechanism.source = extractedExceptionCause.source;
257+
sentryException.mechanism = mechanism;
258+
}
250259

251260
SentryThread? sentryThread;
252261

@@ -262,21 +271,25 @@ class SentryClient {
262271
);
263272
}
264273

265-
sentryExceptions.add(sentryException);
274+
rootException ??= sentryException;
275+
currentException?.addException(sentryException);
276+
currentException = sentryException;
277+
266278
if (sentryThread != null) {
267279
sentryThreads.add(sentryThread);
268280
}
269281
}
270282

271-
event.exceptions = [
272-
...?event.exceptions,
273-
...sentryExceptions,
274-
];
275-
event.threads = [
276-
...?event.threads,
277-
...sentryThreads,
278-
];
279-
return event;
283+
final exceptions = [...?event.exceptions];
284+
if (rootException != null) {
285+
exceptions.add(rootException);
286+
}
287+
return event
288+
..exceptions = exceptions
289+
..threads = [
290+
...?event.threads,
291+
...sentryThreads,
292+
];
280293
}
281294

282295
// The stacktrace is not part of an exception,

0 commit comments

Comments
 (0)