Skip to content

Commit 5612ea6

Browse files
committed
Fix replays and sessions
1 parent 1d5c17d commit 5612ea6

File tree

8 files changed

+79
-43
lines changed

8 files changed

+79
-43
lines changed

flutter/example/lib/main.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,17 @@ Future<void> setupSentry(
8383
// going to log too much for your app, but can be useful when figuring out
8484
// configuration issues, e.g. finding out why your events are not uploaded.
8585
options.debug = true;
86-
options.spotlight = Spotlight(enabled: true);
86+
// options.spotlight = Spotlight(enabled: true);
8787
// options.enableTimeToFullDisplayTracing = true;
8888
options.enableMetrics = true;
89+
options.release = '0.0.2-dart';
8990

9091
options.maxRequestBodySize = MaxRequestBodySize.always;
9192
options.maxResponseBodySize = MaxResponseBodySize.always;
9293
options.navigatorKey = navigatorKey;
9394

94-
options.experimental.replay.sessionSampleRate = 0;
95-
options.experimental.replay.errorSampleRate = 0;
95+
options.experimental.replay.sessionSampleRate = 0.5;
96+
options.experimental.replay.errorSampleRate = 1;
9697

9798
_isIntegrationTest = isIntegrationTest;
9899
if (_isIntegrationTest) {

flutter/lib/src/sentry_flutter.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ mixin SentryFlutter {
143143
if (options.platformChecker.isWeb) {
144144
options.transport = JavascriptEnvelopeTransport(_webBinding!);
145145

146-
// todo: only if replay enabled
147-
options.addEventProcessor(WebReplayEventProcessor(_webBinding!));
146+
options.addEventProcessor(
147+
WebReplayEventProcessor(_webBinding!, options.experimental.replay));
148148
}
149149

150150
options.addEventProcessor(FlutterEnricherEventProcessor(options));

flutter/lib/src/transport/file_system_transport.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// backcompatibility for Flutter < 3.3
22
// ignore: unnecessary_import
3+
import 'dart:convert';
34
import 'dart:typed_data';
45

56
import 'package:flutter/services.dart';

flutter/lib/src/web/factory_noop.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ class NoOpWebInterop implements SentryWebBinding {
1313

1414
@override
1515
Future<void> init(SentryFlutterOptions options) async {}
16+
17+
@override
18+
Future<void> flushReplay() async {}
19+
20+
@override
21+
Future<SentryId> getReplayId() {
22+
return Future.value(SentryId.empty());
23+
}
24+
25+
@override
26+
Future<void> startReplay() async {}
1627
}
1728

1829
SentryWebBinding createBinding(SentryFlutterOptions options) =>

flutter/lib/src/web/sentry_js_bridge.dart

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,35 @@ class SentryJsBridge {
2929
external static _SentryJsClient getClient();
3030

3131
external static JSAny? getReplay();
32+
33+
external static void captureSession();
34+
35+
external static _Scope? getCurrentScope();
36+
37+
external static _Scope? getIsolationScope();
38+
39+
static SentryJsSession? getSession() {
40+
return getCurrentScope()?.getSession() ?? getIsolationScope()?.getSession();
41+
}
3242
}
3343

34-
@JS('Replay')
44+
@JS('Session')
3545
@staticInterop
36-
class _SentryReplay {}
46+
class SentryJsSession {}
47+
48+
extension SentryJsSessionExtension on SentryJsSession {
49+
external JSString status;
50+
51+
external JSNumber errors;
52+
}
53+
54+
@JS('Scope')
55+
@staticInterop
56+
class _Scope {}
57+
58+
extension SentryScopeExtension on _Scope {
59+
external SentryJsSession? getSession();
60+
}
3761

3862
extension SentryReplayExtension on JSAny? {
3963
external void start();
@@ -44,7 +68,7 @@ extension SentryReplayExtension on JSAny? {
4468

4569
external void flush();
4670

47-
external JSString getReplayId();
71+
external JSString? getReplayId();
4872
}
4973

5074
@JS('Client')

flutter/lib/src/web/sentry_web_binding.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ abstract class SentryWebBinding {
1313

1414
Future<void> flushReplay();
1515

16-
Future<void> startReplay();
17-
1816
Future<SentryId> getReplayId();
1917

2018
Future<void> close();

flutter/lib/src/web/sentry_web_interop.dart

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ class SentryWebInterop
4747
'autoSessionTracking': options.enableAutoSessionTracking,
4848
'attachStacktrace': options.attachStacktrace,
4949
'maxBreadcrumbs': options.maxBreadcrumbs,
50-
'replaysSessionSampleRate': 0,
51-
'replaysOnErrorSampleRate': 0,
50+
'replaysSessionSampleRate':
51+
options.experimental.replay.sessionSampleRate,
52+
'replaysOnErrorSampleRate': options.experimental.replay.errorSampleRate,
5253
// using defaultIntegrations ensures the we can control which integrations are added
5354
'defaultIntegrations': [
5455
replay,
@@ -61,38 +62,35 @@ class SentryWebInterop
6162

6263
SentryJsBridge.init(config.jsify());
6364

64-
await startReplay();
65+
// SpotlightBridge.init();
66+
67+
// await startReplay();
6568
});
6669
}
6770

6871
@override
69-
Future<void> captureEvent(SentryEvent event) async {
70-
tryCatchSync('captureEvent', () {
71-
print(event.toJson());
72-
SentryJsBridge.captureEvent(event.toJson().jsify());
73-
});
74-
}
72+
Future<void> captureEvent(SentryEvent event) async {}
7573

7674
@override
7775
Future<void> captureEnvelope(SentryEnvelope envelope) async {
7876
return tryCatchAsync('captureEnvelope', () async {
7977
final List<dynamic> jsItems = [];
8078

8179
for (final item in envelope.items) {
82-
// todo: add support for different type of items
83-
// maybe add a generic to sentryenvelope?
8480
final originalObject = item.originalObject;
85-
final List<dynamic> jsItem = [(await item.header.toJson())];
86-
if (originalObject is SentryTransaction) {
87-
jsItem.add(originalObject.toJson().jsify());
88-
}
81+
jsItems.add([
82+
(await item.header.toJson()),
83+
(await originalObject?.getPayload())
84+
]);
85+
8986
if (originalObject is SentryEvent) {
90-
jsItem.add(originalObject.toJson().jsify());
87+
final session = SentryJsBridge.getSession();
88+
if (envelope.containsUnhandledException) {
89+
session?.status = 'crashed'.toJS;
90+
}
91+
session?.errors = originalObject.exceptions?.length.toJS ?? 0.toJS;
92+
SentryJsBridge.captureSession();
9193
}
92-
if (originalObject is SentryAttachment) {
93-
jsItem.add(await originalObject.bytes);
94-
}
95-
jsItems.add(jsItem);
9694
}
9795

9896
final jsEnvelope = [envelope.header.toJson(), jsItems].jsify();
@@ -108,11 +106,6 @@ class SentryWebInterop
108106
});
109107
}
110108

111-
@override
112-
Future<void> startReplay() async {
113-
replay.startBuffering();
114-
}
115-
116109
@override
117110
Future<void> flushReplay() async {
118111
replay.flush();
@@ -133,6 +126,10 @@ Future<void> _loadSentryScripts(SentryFlutterOptions options,
133126

134127
// todo: put this somewhere else so we can auto-update it as well and only enable non minified bundles in dev mode
135128
final scripts = [
129+
// {
130+
// 'url':
131+
// 'https://unpkg.com/@spotlightjs/overlay@latest/dist/sentry-spotlight.iife.js',
132+
// },
136133
{
137134
'url': 'https://browser.sentry-cdn.com/8.24.0/bundle.tracing.replay.js',
138135
// 'integrity':

flutter/lib/src/web_replay_event_processor.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,36 @@
11
import 'dart:async';
22

33
import '../sentry_flutter.dart';
4+
import 'sentry_replay_options.dart';
45
import 'web/sentry_web_binding.dart';
56

67
class WebReplayEventProcessor implements EventProcessor {
7-
WebReplayEventProcessor(this._binding);
8+
WebReplayEventProcessor(this._binding, this._replayOptions);
89

910
final SentryWebBinding _binding;
11+
final SentryReplayOptions _replayOptions;
12+
bool hasFlushedReplay = false;
1013

1114
@override
1215
FutureOr<SentryEvent?> apply(SentryEvent event, Hint hint) async {
1316
try {
14-
await _binding.flushReplay();
17+
if (!_replayOptions.isEnabled) {
18+
return event;
19+
}
1520

16-
await Future<void>.delayed(Duration(seconds: 1));
21+
if (event.exceptions?.isNotEmpty == true && !hasFlushedReplay) {
22+
await _binding.flushReplay();
23+
hasFlushedReplay = true;
24+
}
1725

1826
final sentryId = await _binding.getReplayId();
1927

20-
print(sentryId);
2128
event = event.copyWith(tags: {
2229
...?event.tags,
2330
'replayId': sentryId.toString(),
2431
});
25-
event.tags?.forEach((key, value) {
26-
print('$key: $value');
27-
});
2832
} catch (exception, stackTrace) {
29-
print('Failed to get replay id: $exception $stackTrace');
33+
// todo: log
3034
}
3135

3236
return event;

0 commit comments

Comments
 (0)