Skip to content

Commit 41db11d

Browse files
Merge c3ff79f into 148f924
2 parents 148f924 + c3ff79f commit 41db11d

File tree

26 files changed

+564
-24
lines changed

26 files changed

+564
-24
lines changed

CHANGELOG.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 5.23.0-alpha.1
4+
5+
### Fixes
6+
7+
- Pass `replaysSessionSampleRate` option to Android ([#3714](https://github.com/getsentry/sentry-react-native/pull/3714))
8+
9+
Access to Mobile Replay 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+
311
## 5.24.1
412

513
### Fixes
@@ -132,6 +140,47 @@ This release does *not* build on iOS. Please use `5.23.1` or newer.
132140
- [changelog](https://github.com/getsentry/sentry-cocoa/blob/main/CHANGELOG.md#8250)
133141
- [diff](https://github.com/getsentry/sentry-cocoa/compare/8.24.0...8.25.0)
134142

143+
## 5.23.0-alpha.0
144+
145+
### Features
146+
147+
- Mobile Session Replay Alpha ([#3714](https://github.com/getsentry/sentry-react-native/pull/3714))
148+
149+
To enable Replay for React Native on mobile and web add the following options.
150+
151+
```js
152+
Sentry.init({
153+
_experiments: {
154+
replaysSessionSampleRate: 1.0,
155+
replaysOnErrorSampleRate: 1.0,
156+
},
157+
});
158+
```
159+
160+
To change the default Mobile Replay options add the `mobileReplayIntegration`.
161+
162+
```js
163+
Sentry.init({
164+
_experiments: {
165+
replaysSessionSampleRate: 1.0,
166+
replaysOnErrorSampleRate: 1.0,
167+
},
168+
integration: [
169+
Sentry.mobileReplayIntegration({
170+
maskAllText: true,
171+
maskAllImages: true,
172+
}),
173+
],
174+
});
175+
```
176+
177+
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/)
178+
179+
### Dependencies
180+
181+
- Bump Cocoa SDK to [8.25.0-alpha.0](https://github.com/getsentry/sentry-cocoa/releases/tag/8.25.0-alpha.0)
182+
- Bump Android SDK to [7.9.0-alpha.1](https://github.com/getsentry/sentry-java/releases/tag/7.9.0-alpha.1)
183+
135184
## 5.22.0
136185

137186
### Features
@@ -642,7 +691,7 @@ This release is compatible with `[email protected]` and newer.
642691
});
643692
```
644693

645-
Read more at https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md#7690
694+
Read more at <https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md#7690>
646695

647696
- Report current screen in `contexts.app.view_names` ([#3339](https://github.com/getsentry/sentry-react-native/pull/3339))
648697

@@ -1033,6 +1082,7 @@ This has been fixed in [version `5.9.1`](https://github.com/getsentry/sentry-rea
10331082
## 5.4.0
10341083

10351084
### Features
1085+
10361086
- Add TS 4.1 typings ([#2995](https://github.com/getsentry/sentry-react-native/pull/2995))
10371087
- TS 3.8 are present and work automatically with older projects
10381088
- Add CPU Info to Device Context ([#2984](https://github.com/getsentry/sentry-react-native/pull/2984))
@@ -2680,7 +2730,7 @@ We are looking into ways making this more stable and plan to re-enable it again
26802730

26812731
## v0.23.2
26822732

2683-
- Fixed #228 again ¯\\_(ツ)_
2733+
- Fixed #228 again ¯\\*(ツ)*
26842734

26852735
## v0.23.1
26862736

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ android {
5454

5555
dependencies {
5656
implementation 'com.facebook.react:react-native:+'
57-
api 'io.sentry:sentry-android:7.10.0'
57+
api 'io.sentry:sentry-android:7.11.0-alpha.2'
5858
}

android/src/main/java/io/sentry/react/RNSentryModuleImpl.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import io.sentry.SentryExecutorService;
6262
import io.sentry.SentryLevel;
6363
import io.sentry.SentryOptions;
64+
import io.sentry.SentryReplayOptions;
6465
import io.sentry.UncaughtExceptionHandlerIntegration;
6566
import io.sentry.android.core.AndroidLogger;
6667
import io.sentry.android.core.AndroidProfiler;
@@ -79,6 +80,7 @@
7980
import io.sentry.android.core.performance.AppStartMetrics;
8081
import io.sentry.protocol.SdkVersion;
8182
import io.sentry.protocol.SentryException;
83+
import io.sentry.protocol.SentryId;
8284
import io.sentry.protocol.SentryPackage;
8385
import io.sentry.protocol.User;
8486
import io.sentry.protocol.ViewHierarchy;
@@ -252,7 +254,9 @@ public void initNativeSdk(final ReadableMap rnOptions, Promise promise) {
252254
if (rnOptions.hasKey("enableNdk")) {
253255
options.setEnableNdk(rnOptions.getBoolean("enableNdk"));
254256
}
255-
257+
if (rnOptions.hasKey("_experiments")) {
258+
options.getExperimental().setSessionReplay(getReplayOptions(rnOptions));
259+
}
256260
options.setBeforeSend((event, hint) -> {
257261
// React native internally throws a JavascriptException
258262
// Since we catch it before that, we don't want to send this one
@@ -293,6 +297,37 @@ public void initNativeSdk(final ReadableMap rnOptions, Promise promise) {
293297
promise.resolve(true);
294298
}
295299

300+
private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
301+
@NotNull final SentryReplayOptions androidReplayOptions = new SentryReplayOptions();
302+
303+
@Nullable final ReadableMap rnExperimentsOptions = rnOptions.getMap("_experiments");
304+
if (rnExperimentsOptions == null) {
305+
return androidReplayOptions;
306+
}
307+
308+
if (!(rnExperimentsOptions.hasKey("replaysSessionSampleRate") || rnExperimentsOptions.hasKey("replaysOnErrorSampleRate"))) {
309+
return androidReplayOptions;
310+
}
311+
312+
androidReplayOptions.setSessionSampleRate(rnExperimentsOptions.hasKey("replaysSessionSampleRate")
313+
? rnExperimentsOptions.getDouble("replaysSessionSampleRate") : null);
314+
androidReplayOptions.setErrorSampleRate(rnExperimentsOptions.hasKey("replaysOnErrorSampleRate")
315+
? rnExperimentsOptions.getDouble("replaysOnErrorSampleRate") : null);
316+
317+
if (!rnOptions.hasKey("mobileReplayOptions")) {
318+
return androidReplayOptions;
319+
}
320+
@Nullable final ReadableMap rnMobileReplayOptions = rnOptions.getMap("mobileReplayOptions");
321+
if (rnMobileReplayOptions == null) {
322+
return androidReplayOptions;
323+
}
324+
325+
androidReplayOptions.setRedactAllText(!rnMobileReplayOptions.hasKey("maskAllText") || rnMobileReplayOptions.getBoolean("maskAllText"));
326+
androidReplayOptions.setRedactAllImages(!rnMobileReplayOptions.hasKey("maskAllImages") || rnMobileReplayOptions.getBoolean("maskAllImages"));
327+
328+
return androidReplayOptions;
329+
}
330+
296331
public void crash() {
297332
throw new RuntimeException("TEST - Sentry Client Crash (only works in release mode)");
298333
}
@@ -394,6 +429,24 @@ public void fetchNativeFrames(Promise promise) {
394429
}
395430
}
396431

432+
public void captureReplay(boolean isHardCrash, Promise promise) {
433+
Sentry.getCurrentHub().getOptions().getReplayController().sendReplay(isHardCrash, null, null);
434+
promise.resolve(getCurrentReplayId());
435+
}
436+
437+
public @Nullable String getCurrentReplayId() {
438+
final @Nullable IScope scope = InternalSentrySdk.getCurrentScope();
439+
if (scope == null) {
440+
return null;
441+
}
442+
443+
final @NotNull SentryId id = scope.getReplayId();
444+
if (id == SentryId.EMPTY_ID) {
445+
return null;
446+
}
447+
return id.toString();
448+
}
449+
397450
public void captureEnvelope(String rawBytes, ReadableMap options, Promise promise) {
398451
byte[] bytes = Base64.decode(rawBytes, Base64.DEFAULT);
399452

android/src/newarch/java/io/sentry/react/RNSentryModule.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,14 @@ public WritableMap fetchNativeStackFramesBy(ReadableArray instructionsAddr) {
158158
// Not used on Android
159159
return null;
160160
}
161+
162+
@Override
163+
public void captureReplay(boolean isHardCrash, Promise promise) {
164+
this.impl.captureReplay(isHardCrash, promise);
165+
}
166+
167+
@Override
168+
public String getCurrentReplayId() {
169+
return this.impl.getCurrentReplayId();
170+
}
161171
}

android/src/oldarch/java/io/sentry/react/RNSentryModule.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,14 @@ public WritableMap fetchNativeStackFramesBy(ReadableArray instructionsAddr) {
158158
// Not used on Android
159159
return null;
160160
}
161+
162+
@ReactMethod
163+
public void captureReplay(boolean isHardCrash, Promise promise) {
164+
this.impl.captureReplay(isHardCrash, promise);
165+
}
166+
167+
@ReactMethod(isBlockingSynchronousMethod = true)
168+
public String getCurrentReplayId() {
169+
return this.impl.getCurrentReplayId();
170+
}
161171
}

ios/RNSentry.mm

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
117117
// Because we sent it already before the app crashed.
118118
if (nil != event.exceptions.firstObject.type &&
119119
[event.exceptions.firstObject.type rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) {
120-
NSLog(@"Unhandled JS Exception");
121120
return nil;
122121
}
123122

@@ -136,6 +135,28 @@ - (SentryOptions *_Nullable)createOptionsWithDictionary:(NSDictionary *_Nonnull)
136135
[mutableOptions removeObjectForKey:@"tracesSampler"];
137136
[mutableOptions removeObjectForKey:@"enableTracing"];
138137

138+
if ([mutableOptions valueForKey:@"_experiments"] != nil) {
139+
NSDictionary *experiments = mutableOptions[@"_experiments"];
140+
if (experiments[@"replaysSessionSampleRate"] != nil || experiments[@"replaysOnErrorSampleRate"] != nil) {
141+
[mutableOptions setValue:@{
142+
@"sessionReplay": @{
143+
@"sessionSampleRate": experiments[@"replaysSessionSampleRate"] ?: [NSNull null],
144+
@"errorSampleRate": experiments[@"replaysOnErrorSampleRate"] ?: [NSNull null],
145+
@"redactAllImages": mutableOptions[@"mobileReplayOptions"] != nil &&
146+
mutableOptions[@"mobileReplayOptions"][@"maskAllImages"] != nil
147+
? mutableOptions[@"mobileReplayOptions"][@"maskAllImages"]
148+
: [NSNull null],
149+
@"redactAllText": mutableOptions[@"mobileReplayOptions"] != nil &&
150+
mutableOptions[@"mobileReplayOptions"][@"maskAllText"] != nil
151+
? mutableOptions[@"mobileReplayOptions"][@"maskAllText"]
152+
: [NSNull null],
153+
}
154+
} forKey:@"experimental"];
155+
[self addReplayRNRedactClasses: mutableOptions[@"mobileReplayOptions"]];
156+
}
157+
[mutableOptions removeObjectForKey:@"_experiments"];
158+
}
159+
139160
SentryOptions *sentryOptions = [[SentryOptions alloc] initWithDict:mutableOptions didFailWithError:errorPointer];
140161
if (*errorPointer != nil) {
141162
return nil;
@@ -610,6 +631,31 @@ - (NSDictionary*) fetchNativeStackFramesBy: (NSArray<NSNumber*>*)instructionsAdd
610631
// the 'tracesSampleRate' or 'tracesSampler' option.
611632
}
612633

634+
RCT_EXPORT_METHOD(captureReplay: (BOOL)isHardCrash
635+
resolver:(RCTPromiseResolveBlock)resolve
636+
rejecter:(RCTPromiseRejectBlock)reject)
637+
{
638+
[PrivateSentrySDKOnly captureReplay];
639+
resolve([PrivateSentrySDKOnly getReplayId]);
640+
}
641+
642+
RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString *, getCurrentReplayId)
643+
{
644+
return [PrivateSentrySDKOnly getReplayId];
645+
}
646+
647+
- (void) addReplayRNRedactClasses: (NSDictionary *_Nullable)replayOptions
648+
{
649+
NSMutableArray *_Nonnull classesToRedact = [[NSMutableArray alloc] init];
650+
if ([replayOptions[@"maskAllImages"] boolValue] == YES) {
651+
[classesToRedact addObject: NSClassFromString(@"RCTImageView")];
652+
}
653+
if ([replayOptions[@"maskAllText"] boolValue] == YES) {
654+
[classesToRedact addObject: NSClassFromString(@"RCTTextView")];
655+
}
656+
[PrivateSentrySDKOnly addReplayRedactClasses: classesToRedact];
657+
}
658+
613659
static NSString* const enabledProfilingMessage = @"Enable Hermes to use Sentry Profiling.";
614660
static SentryId* nativeProfileTraceId = nil;
615661
static uint64_t nativeProfileStartTime = 0;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@sentry/react-native",
33
"homepage": "https://github.com/getsentry/sentry-react-native",
44
"repository": "https://github.com/getsentry/sentry-react-native",
5-
"version": "5.24.1",
5+
"version": "5.23.0-alpha.1",
66
"description": "Official Sentry SDK for react-native",
77
"typings": "dist/js/index.d.ts",
88
"types": "dist/js/index.d.ts",

samples/expo/app.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"slug": "sentry-react-native-expo-sample",
55
"jsEngine": "hermes",
66
"scheme": "sentry-expo-sample",
7-
"version": "5.24.1",
7+
"version": "5.23.0-alpha.1",
88
"orientation": "portrait",
99
"icon": "./assets/icon.png",
1010
"userInterfaceStyle": "light",
@@ -59,4 +59,4 @@
5959
]
6060
]
6161
}
62-
}
62+
}

samples/expo/app/_layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ process.env.EXPO_SKIP_DURING_EXPORT !== 'true' && Sentry.init({
7878
// dist: `1`,
7979
_experiments: {
8080
profilesSampleRate: 0,
81+
// replaysOnErrorSampleRate: 1.0,
82+
replaysSessionSampleRate: 1.0,
8183
},
8284
enableSpotlight: true,
8385
});

samples/expo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sentry-react-native-expo-sample",
3-
"version": "5.24.1",
3+
"version": "5.23.0-alpha.1",
44
"main": "expo-router/entry",
55
"scripts": {
66
"start": "expo start",

0 commit comments

Comments
 (0)