Skip to content
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

Move replay and privacy from experimental to options #2755

Merged
merged 5 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
@@ -1,5 +1,9 @@
# Changelog

## 9.0.0 Unreleased

- Move replay and privacy from experimental to options ([#2755](https://github.com/getsentry/sentry-dart/pull/2755))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 🚫 The changelog entry seems to be part of an already released section ## 9.0.0.
    Consider moving the entry to the ## Unreleased section, please.


## 9.0.0-alpha.1

### Breaking changes
Expand Down
2 changes: 1 addition & 1 deletion flutter/example/integration_test/replay_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void main() {
options.automatedTestMode = true;
options.dsn = 'https://[email protected]/1234567';
options.debug = true;
options.experimental.replay.sessionSampleRate = 1.0;
options.replay.sessionSampleRate = 1.0;
});
});

Expand Down
8 changes: 2 additions & 6 deletions flutter/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,8 @@ Future<void> setupSentry(
options.maxRequestBodySize = MaxRequestBodySize.always;
options.navigatorKey = navigatorKey;

options.experimental.replay.sessionSampleRate = 1.0;
options.experimental.replay.onErrorSampleRate = 1.0;

// This has a side-effect of creating the default privacy configuration,
// thus enabling Screenshot masking. No need to actually change it.
options.experimental.privacy;
options.replay.sessionSampleRate = 1.0;
options.replay.onErrorSampleRate = 1.0;

_isIntegrationTest = isIntegrationTest;
if (_isIntegrationTest) {
Expand Down
3 changes: 1 addition & 2 deletions flutter/lib/src/native/cocoa/cocoa_replay_recorder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ class CocoaReplayRecorder {
CocoaReplayRecorder(this._options)
: _recorder = ReplayScreenshotRecorder(
ScreenshotRecorderConfig(
pixelRatio:
_options.experimental.replay.quality.resolutionScalingFactor,
pixelRatio: _options.replay.quality.resolutionScalingFactor,
),
_options,
);
Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/native/cocoa/sentry_native_cocoa.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class SentryNativeCocoa extends SentryNativeChannel {
Future<void> init(Hub hub) async {
// We only need these when replay is enabled (session or error capture)
// so let's set it up conditionally. This allows Dart to trim the code.
if (options.experimental.replay.isEnabled) {
if (options.replay.isEnabled) {
channel.setMethodCallHandler((call) async {
switch (call.method) {
case 'captureReplayScreenshot':
Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/native/java/sentry_native_java.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class SentryNativeJava extends SentryNativeChannel {
Future<void> init(Hub hub) async {
// We only need these when replay is enabled (session or error capture)
// so let's set it up conditionally. This allows Dart to trim the code.
if (options.experimental.replay.isEnabled) {
if (options.replay.isEnabled) {
channel.setMethodCallHandler((call) async {
switch (call.method) {
case 'ReplayRecorder.start':
Expand Down
16 changes: 8 additions & 8 deletions flutter/lib/src/native/sentry_native_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,15 @@ class SentryNativeChannel
options.appHangTimeoutInterval.inMilliseconds,
if (options.proxy != null) 'proxy': options.proxy?.toJson(),
'replay': <String, dynamic>{
'quality': options.experimental.replay.quality.name,
'sessionSampleRate': options.experimental.replay.sessionSampleRate,
'onErrorSampleRate': options.experimental.replay.onErrorSampleRate,
'quality': options.replay.quality.name,
'sessionSampleRate': options.replay.sessionSampleRate,
'onErrorSampleRate': options.replay.onErrorSampleRate,
'tags': <String, dynamic>{
'maskAllText': options.experimental.privacy.maskAllText,
'maskAllImages': options.experimental.privacy.maskAllImages,
'maskAssetImages': options.experimental.privacy.maskAssetImages,
if (options.experimental.privacy.userMaskingRules.isNotEmpty)
'maskingRules': options.experimental.privacy.userMaskingRules
'maskAllText': options.privacy.maskAllText,
'maskAllImages': options.privacy.maskAllImages,
'maskAssetImages': options.privacy.maskAssetImages,
if (options.privacy.userMaskingRules.isNotEmpty)
'maskingRules': options.privacy.userMaskingRules
.map((rule) => '${rule.name}: ${rule.description}')
.toList(growable: false),
},
Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/replay/integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ReplayIntegration extends Integration<SentryFlutterOptions> {

@override
FutureOr<void> call(Hub hub, SentryFlutterOptions options) {
final replayOptions = options.experimental.replay;
final replayOptions = options.replay;
if (_native.supportsReplay && replayOptions.isEnabled) {
options.sdk.addIntegration(replayIntegrationName);

Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/replay/replay_recorder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var _instanceCounter = 0;
class ReplayScreenshotRecorder extends ScreenshotRecorder {
ReplayScreenshotRecorder(super.config, super.options)
: super(
privacyOptions: options.experimental.privacy,
privacyOptions: options.privacy,
logName: 'ReplayRecorder #${++_instanceCounter}');

@override
Expand Down
2 changes: 1 addition & 1 deletion flutter/lib/src/screenshot/recorder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ScreenshotRecorder {
SentryPrivacyOptions? privacyOptions,
this.logName = 'ScreenshotRecorder',
}) {
privacyOptions ??= options.experimental.privacy;
privacyOptions ??= options.privacy;

final maskingConfig =
privacyOptions.buildMaskingConfig(_log, options.platformChecker);
Expand Down
22 changes: 7 additions & 15 deletions flutter/lib/src/sentry_flutter_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,13 @@ class SentryFlutterOptions extends SentryOptions {
/// you must use `SentryWidgetsFlutterBinding.ensureInitialized()` instead.
bool enableFramesTracking = true;

/// Replay recording configuration.
final replay = SentryReplayOptions();

/// Privacy configuration for masking sensitive data in screenshots and Session Replay.
/// Screen content masking is enabled by default.
final privacy = SentryPrivacyOptions();

/// By using this, you are disabling native [Breadcrumb] tracking and instead
/// you are just tracking [Breadcrumb]s which result from events available
/// in the current Flutter environment.
Expand Down Expand Up @@ -368,21 +375,6 @@ class SentryFlutterOptions extends SentryOptions {
@override
// ignore: invalid_use_of_internal_member
set automatedTestMode(bool value) => super.automatedTestMode = value;

/// Configuration of experimental features that may change or be removed
/// without prior notice. Additionally, these features may not be ready for
/// production use yet.
@meta.experimental
final experimental = _SentryFlutterExperimentalOptions();
}

class _SentryFlutterExperimentalOptions {
/// Replay recording configuration.
final replay = SentryReplayOptions();

/// Privacy configuration for masking sensitive data in screenshots and Session Replay.
/// Screen content masking is enabled by default.
final privacy = SentryPrivacyOptions();
}

/// A callback which can be used to suppress capturing of screenshots.
Expand Down
12 changes: 2 additions & 10 deletions flutter/lib/src/sentry_privacy_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,14 @@ class SentryPrivacyOptions {
/// Mask all text content. Draws a rectangle of text bounds with text color
/// on top. Currently, only [Text] and [EditableText] Widgets are masked.
/// Default is enabled.
@experimental
var maskAllText = true;

/// Mask content of all images. Draws a rectangle of image bounds with image's
/// dominant color on top. Currently, only [Image] widgets are masked.
/// Default is enabled (except for asset images, see [maskAssetImages]).
@experimental
var maskAllImages = true;

/// Redact asset images coming from the root asset bundle.
@experimental
var maskAssetImages = false;

final _userMaskingRules = <SentryMaskingRule>[];
Expand Down Expand Up @@ -80,11 +77,6 @@ class SentryPrivacyOptions {
final regexp = RegExp('video|webview|password|pinput|camera|chart',
caseSensitive: false);

// Note: the following line just makes sure if the option is renamed,
// someone will notice that there is a string that needs updating too.
SentryFlutterOptions().experimental.privacy;
final optionsName = 'options.experimental.privacy';

rules.add(SentryMaskingCustomRule<Widget>(
callback: (Element element, Widget widget) {
final type = widget.runtimeType.toString();
Expand All @@ -96,9 +88,9 @@ class SentryPrivacyOptions {
'widget comes from a third-party plugin or your code, Sentry '
"doesn't recognize it and can't reliably mask it in release "
'builds (due to obfuscation). '
'Please mask it explicitly using $optionsName.mask<$type>(). '
'Please mask it explicitly using options.privacy.mask<$type>(). '
'If you want to silence this warning and keep the widget '
'visible in captures, you can use $optionsName.unmask<$type>(). '
'visible in captures, you can use options.privacy.unmask<$type>(). '
'Note: the RegExp matched is: $regexp (case insensitive).');
}
return SentryMaskingDecision.continueProcessing;
Expand Down
3 changes: 1 addition & 2 deletions flutter/lib/src/sentry_replay_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import 'package:meta/meta.dart';

import 'replay/replay_quality.dart';

/// Configuration of the experimental replay feature.
@experimental
/// Configuration of the replay feature.
class SentryReplayOptions {
double? _sessionSampleRate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ void main() {

testWidgets('adds screenshot attachment with masking enabled dart:io',
(tester) async {
fixture.options.experimental.privacy.maskAllText = true;
fixture.options.privacy.maskAllText = true;
await _addScreenshotAttachment(tester, null, added: true, isWeb: false);
});

Expand Down
8 changes: 4 additions & 4 deletions flutter/test/integrations/init_native_sdk_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ void main() {
user: 'admin',
pass: '0000',
)
..experimental.replay.quality = SentryReplayQuality.high
..experimental.replay.sessionSampleRate = 0.1
..experimental.replay.onErrorSampleRate = 0.2
..experimental.privacy.mask<Image>()
..replay.quality = SentryReplayQuality.high
..replay.sessionSampleRate = 0.1
..replay.onErrorSampleRate = 0.2
..privacy.mask<Image>()
..spotlight =
Spotlight(enabled: true, url: 'http://localhost:8969/stream');

Expand Down
Loading
Loading