Skip to content

Commit 2886956

Browse files
authored
Merge branch 'main' into fix/flutter-multiview-support
2 parents 98bc647 + 3356741 commit 2886956

File tree

77 files changed

+2578
-695
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2578
-695
lines changed

CHANGELOG.md

+85-22
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Session replay Alpha for Android and iOS ([#2208](https://github.com/getsentry/sentry-dart/pull/2208)).
8+
9+
To try out replay, you can set following options (access is limited to early access orgs on Sentry. If you're interested, [sign up for the waitlist](https://sentry.io/lp/mobile-replay-beta/)):
10+
11+
```dart
12+
await SentryFlutter.init(
13+
(options) {
14+
...
15+
options.experimental.replay.sessionSampleRate = 1.0;
16+
options.experimental.replay.errorSampleRate = 1.0;
17+
},
18+
appRunner: () => runApp(MyApp()),
19+
);
20+
```
21+
522
### Dependencies
623

724
- Bump Cocoa SDK from v8.35.1 to v8.36.0 ([#2252](https://github.com/getsentry/sentry-dart/pull/2252))
@@ -13,13 +30,14 @@
1330
### Features
1431

1532
- Add `SentryFlutter.nativeCrash()` using MethodChannels for Android and iOS ([#2239](https://github.com/getsentry/sentry-dart/pull/2239))
16-
- This can be used to test if native crash reporting works
33+
- This can be used to test if native crash reporting works
1734
- Add `ignoreRoutes` parameter to `SentryNavigatorObserver`. ([#2218](https://github.com/getsentry/sentry-dart/pull/2218))
18-
- This will ignore the Routes and prevent the Route from being pushed to the Sentry server.
19-
- Ignored routes will also create no TTID and TTFD spans.
20-
```dart
21-
SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
22-
```
35+
- This will ignore the Routes and prevent the Route from being pushed to the Sentry server.
36+
- Ignored routes will also create no TTID and TTFD spans.
37+
38+
```dart
39+
SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
40+
```
2341

2442
### Improvements
2543

@@ -34,12 +52,33 @@ SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
3452
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7140)
3553
- [diff](https://github.com/getsentry/sentry-java/compare/7.13.0...7.14.0)
3654

55+
## 8.8.0-alpha.1
56+
57+
### Features
58+
59+
- iOS Session Replay Alpha ([#2209](https://github.com/getsentry/sentry-dart/pull/2209))
60+
- Android replay touch tracking support ([#2228](https://github.com/getsentry/sentry-dart/pull/2228))
61+
- Add `ignoreRoutes` parameter to `SentryNavigatorObserver`. ([#2218](https://github.com/getsentry/sentry-dart/pull/2218))
62+
- This will ignore the Routes and prevent the Route from being pushed to the Sentry server.
63+
- Ignored routes will also create no TTID and TTFD spans.
64+
65+
```dart
66+
SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
67+
```
68+
69+
### Dependencies
70+
71+
- Bump Android SDK from v7.13.0 to v7.14.0 ([#2228](https://github.com/getsentry/sentry-dart/pull/2228))
72+
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7140)
73+
- [diff](https://github.com/getsentry/sentry-java/compare/7.13.0...7.14.0)
74+
3775
## 8.7.0
3876

3977
### Features
4078

4179
- Add support for span level measurements. ([#2214](https://github.com/getsentry/sentry-dart/pull/2214))
4280
- Add `ignoreTransactions` and `ignoreErrors` to options ([#2207](https://github.com/getsentry/sentry-dart/pull/2207))
81+
4382
```dart
4483
await SentryFlutter.init(
4584
(options) {
@@ -51,8 +90,10 @@ SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
5190
appRunner: () => runApp(MyApp()),
5291
);
5392
```
93+
5494
- Add proxy support ([#2192](https://github.com/getsentry/sentry-dart/pull/2192))
5595
- Configure a `SentryProxy` object and set it on `SentryFlutter.init`
96+
5697
```dart
5798
import 'package:flutter/widgets.dart';
5899
import 'package:sentry_flutter/sentry_flutter.dart';
@@ -92,24 +133,25 @@ SentryNavigatorObserver(ignoreRoutes: ["/ignoreThisRoute"]),
92133
- This is enabled automatically and will change grouping if you already have issues with obfuscated titles
93134
- If you want to disable this feature, set `enableExceptionTypeIdentification` to `false` in your Sentry options
94135
- You can add your custom exception identifier if there are exceptions that we do not identify out of the box
95-
```dart
96-
// How to add your own custom exception identifier
97-
class MyCustomExceptionIdentifier implements ExceptionIdentifier {
98-
@override
99-
String? identifyType(Exception exception) {
100-
if (exception is MyCustomException) {
101-
return 'MyCustomException';
102-
}
103-
if (exception is MyOtherCustomException) {
104-
return 'MyOtherCustomException';
136+
137+
```dart
138+
// How to add your own custom exception identifier
139+
class MyCustomExceptionIdentifier implements ExceptionIdentifier {
140+
@override
141+
String? identifyType(Exception exception) {
142+
if (exception is MyCustomException) {
143+
return 'MyCustomException';
144+
}
145+
if (exception is MyOtherCustomException) {
146+
return 'MyOtherCustomException';
147+
}
148+
return null;
105149
}
106-
return null;
107150
}
108-
}
109151
110-
SentryFlutter.init((options) =>
111-
options..prependExceptionTypeIdentifier(MyCustomExceptionIdentifier()));
112-
```
152+
SentryFlutter.init((options) =>
153+
options..prependExceptionTypeIdentifier(MyCustomExceptionIdentifier()));
154+
```
113155

114156
### Deprecated
115157

@@ -125,6 +167,27 @@ SentryFlutter.init((options) =>
125167
- [changelog](https://github.com/getsentry/sentry-java/blob/main/CHANGELOG.md#7130)
126168
- [diff](https://github.com/getsentry/sentry-java/compare/7.12.0...7.13.0)
127169

170+
## 8.6.0-alpha.2
171+
172+
### Features
173+
174+
- Android Session Replay Alpha ([#2032](https://github.com/getsentry/sentry-dart/pull/2032))
175+
176+
To try out replay, you can set following options:
177+
178+
```dart
179+
await SentryFlutter.init(
180+
(options) {
181+
...
182+
options.experimental.replay.sessionSampleRate = 1.0;
183+
options.experimental.replay.errorSampleRate = 1.0;
184+
},
185+
appRunner: () => runApp(MyApp()),
186+
);
187+
```
188+
189+
Access is limited to early access orgs on Sentry. If you're interested, [sign up for the waitlist](https://sentry.io/lp/mobile-replay-beta/)
190+
128191
## 8.5.0
129192

130193
### Features
@@ -137,7 +200,7 @@ SentryFlutter.init((options) =>
137200
### Fixes
138201

139202
- Disable sff & frame delay detection on web, linux and windows ([#2182](https://github.com/getsentry/sentry-dart/pull/2182))
140-
- Display refresh rate is locked at 60 for these platforms which can lead to inaccurate metrics
203+
- Display refresh rate is locked at 60 for these platforms which can lead to inaccurate metrics
141204

142205
### Improvements
143206

dart/lib/src/protocol/breadcrumb.dart

+30
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ class Breadcrumb {
5252
String? httpQuery,
5353
String? httpFragment,
5454
}) {
55+
// The timestamp is used as the request-end time, so we need to set it right
56+
// now and not rely on the default constructor.
57+
timestamp ??= getUtcDateTime();
58+
5559
return Breadcrumb(
5660
type: 'http',
5761
category: 'http',
@@ -67,6 +71,11 @@ class Breadcrumb {
6771
if (responseBodySize != null) 'response_body_size': responseBodySize,
6872
if (httpQuery != null) 'http.query': httpQuery,
6973
if (httpFragment != null) 'http.fragment': httpFragment,
74+
if (requestDuration != null)
75+
'start_timestamp':
76+
timestamp.millisecondsSinceEpoch - requestDuration.inMilliseconds,
77+
if (requestDuration != null)
78+
'end_timestamp': timestamp.millisecondsSinceEpoch,
7079
},
7180
);
7281
}
@@ -97,11 +106,32 @@ class Breadcrumb {
97106
String? viewClass,
98107
}) {
99108
final newData = data ?? {};
109+
var path = '';
110+
100111
if (viewId != null) {
101112
newData['view.id'] = viewId;
113+
path = viewId;
114+
}
115+
116+
if (newData.containsKey('label')) {
117+
if (path.isEmpty) {
118+
path = newData['label'];
119+
} else {
120+
path = "$path, label: ${newData['label']}";
121+
}
102122
}
123+
103124
if (viewClass != null) {
104125
newData['view.class'] = viewClass;
126+
if (path.isEmpty) {
127+
path = viewClass;
128+
} else {
129+
path = "$viewClass($path)";
130+
}
131+
}
132+
133+
if (path.isNotEmpty && !newData.containsKey('path')) {
134+
newData['path'] = path;
105135
}
106136

107137
return Breadcrumb(

dart/lib/src/protocol/sentry_trace_context.dart

+13-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class SentryTraceContext {
1818
/// Id of a parent span
1919
final SpanId? parentSpanId;
2020

21+
/// Replay associated with this trace.
22+
final SentryId? replayId;
23+
2124
/// Whether the span is sampled or not
2225
final bool? sampled;
2326

@@ -50,6 +53,9 @@ class SentryTraceContext {
5053
? null
5154
: SpanId.fromId(json['parent_span_id'] as String),
5255
traceId: SentryId.fromId(json['trace_id'] as String),
56+
replayId: json['replay_id'] == null
57+
? null
58+
: SentryId.fromId(json['replay_id'] as String),
5359
description: json['description'] as String?,
5460
status: json['status'] == null
5561
? null
@@ -68,6 +74,7 @@ class SentryTraceContext {
6874
'trace_id': traceId.toString(),
6975
'op': operation,
7076
if (parentSpanId != null) 'parent_span_id': parentSpanId!.toString(),
77+
if (replayId != null) 'replay_id': replayId!.toString(),
7178
if (description != null) 'description': description,
7279
if (status != null) 'status': status!.toString(),
7380
if (origin != null) 'origin': origin,
@@ -84,6 +91,7 @@ class SentryTraceContext {
8491
sampled: sampled,
8592
origin: origin,
8693
unknown: unknown,
94+
replayId: replayId,
8795
);
8896

8997
SentryTraceContext({
@@ -96,16 +104,17 @@ class SentryTraceContext {
96104
this.status,
97105
this.origin,
98106
this.unknown,
107+
this.replayId,
99108
}) : traceId = traceId ?? SentryId.newId(),
100109
spanId = spanId ?? SpanId.newId();
101110

102111
@internal
103112
factory SentryTraceContext.fromPropagationContext(
104113
PropagationContext propagationContext) {
105114
return SentryTraceContext(
106-
traceId: propagationContext.traceId,
107-
spanId: propagationContext.spanId,
108-
operation: 'default',
109-
);
115+
traceId: propagationContext.traceId,
116+
spanId: propagationContext.spanId,
117+
operation: 'default',
118+
replayId: propagationContext.baggage?.getReplayId());
110119
}
111120
}

dart/lib/src/scope.dart

+10-1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ class Scope {
9797
/// they must be JSON-serializable.
9898
Map<String, dynamic> get extra => Map.unmodifiable(_extra);
9999

100+
/// Active replay recording.
101+
@internal
102+
SentryId? get replayId => _replayId;
103+
@internal
104+
set replayId(SentryId? value) => _replayId = value;
105+
SentryId? _replayId;
106+
100107
final Contexts _contexts = Contexts();
101108

102109
/// Unmodifiable map of the scope contexts key/value
@@ -237,6 +244,7 @@ class Scope {
237244
_tags.clear();
238245
_extra.clear();
239246
_eventProcessors.clear();
247+
_replayId = null;
240248

241249
_clearBreadcrumbsSync();
242250
_setUserSync(null);
@@ -429,7 +437,8 @@ class Scope {
429437
..fingerprint = List.from(fingerprint)
430438
.._transaction = _transaction
431439
..span = span
432-
.._enableScopeSync = false;
440+
.._enableScopeSync = false
441+
.._replayId = _replayId;
433442

434443
clone._setUserSync(user);
435444

dart/lib/src/sentry_baggage.dart

+10
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ class SentryBaggage {
111111
// ignore: deprecated_member_use_from_same_package
112112
setUserSegment(scope.user!.segment!);
113113
}
114+
if (scope.replayId != null && scope.replayId != SentryId.empty()) {
115+
setReplayId(scope.replayId.toString());
116+
}
114117
}
115118

116119
static Map<String, String> _extractKeyValuesFromBaggageString(
@@ -205,5 +208,12 @@ class SentryBaggage {
205208
return double.tryParse(sampleRate);
206209
}
207210

211+
void setReplayId(String value) => set('sentry-replay_id', value);
212+
213+
SentryId? getReplayId() {
214+
final replayId = get('sentry-replay_id');
215+
return replayId == null ? null : SentryId.fromId(replayId);
216+
}
217+
208218
Map<String, String> get keyValues => Map.unmodifiable(_keyValues);
209219
}

dart/lib/src/sentry_client.dart

+5-5
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,15 @@ class SentryClient {
167167

168168
var traceContext = scope?.span?.traceContext();
169169
if (traceContext == null) {
170-
if (scope?.propagationContext.baggage == null) {
171-
scope?.propagationContext.baggage =
172-
SentryBaggage({}, logger: _options.logger);
173-
scope?.propagationContext.baggage?.setValuesFromScope(scope, _options);
174-
}
175170
if (scope != null) {
171+
scope.propagationContext.baggage ??=
172+
SentryBaggage({}, logger: _options.logger)
173+
..setValuesFromScope(scope, _options);
176174
traceContext = SentryTraceContextHeader.fromBaggage(
177175
scope.propagationContext.baggage!);
178176
}
177+
} else {
178+
traceContext.replayId = scope?.replayId;
179179
}
180180

181181
final envelope = SentryEnvelope.fromEvent(

0 commit comments

Comments
 (0)