-
-
Notifications
You must be signed in to change notification settings - Fork 327
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(replay): Add breadcrumbs mapping from RN to RRWeb format (#3846)
- Loading branch information
Showing
9 changed files
with
272 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
android/src/main/java/io/sentry/react/RNSentryReplayBreadcrumbConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package io.sentry.react; | ||
|
||
import io.sentry.Breadcrumb; | ||
import io.sentry.android.replay.DefaultReplayBreadcrumbConverter; | ||
import io.sentry.rrweb.RRWebEvent; | ||
import io.sentry.rrweb.RRWebBreadcrumbEvent; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
public final class RNSentryReplayBreadcrumbConverter extends DefaultReplayBreadcrumbConverter { | ||
public RNSentryReplayBreadcrumbConverter() { | ||
} | ||
|
||
@Override | ||
public @Nullable RRWebEvent convert(final @NotNull Breadcrumb breadcrumb) { | ||
RRWebBreadcrumbEvent rrwebBreadcrumb = new RRWebBreadcrumbEvent(); | ||
assert rrwebBreadcrumb.getCategory() == null; | ||
|
||
if (breadcrumb.getCategory().equals("touch")) { | ||
rrwebBreadcrumb.setCategory("ui.tap"); | ||
ArrayList path = (ArrayList) breadcrumb.getData("path"); | ||
if (path != null) { | ||
StringBuilder message = new StringBuilder(); | ||
for (int i = Math.min(3, path.size()); i >= 0; i--) { | ||
HashMap item = (HashMap) path.get(i); | ||
message.append(item.get("name")); | ||
if (item.containsKey("element") || item.containsKey("file")) { | ||
message.append('('); | ||
if (item.containsKey("element")) { | ||
message.append(item.get("element")); | ||
if (item.containsKey("file")) { | ||
message.append(", "); | ||
message.append(item.get("file")); | ||
} | ||
} else if (item.containsKey("file")) { | ||
message.append(item.get("file")); | ||
} | ||
message.append(')'); | ||
} | ||
if (i > 0) { | ||
message.append(" > "); | ||
} | ||
} | ||
rrwebBreadcrumb.setMessage(message.toString()); | ||
} | ||
rrwebBreadcrumb.setData(breadcrumb.getData()); | ||
} else if (breadcrumb.getCategory().equals("navigation")) { | ||
rrwebBreadcrumb.setCategory(breadcrumb.getCategory()); | ||
rrwebBreadcrumb.setData(breadcrumb.getData()); | ||
} | ||
|
||
if (rrwebBreadcrumb.getCategory() != null && !rrwebBreadcrumb.getCategory().isEmpty()) { | ||
rrwebBreadcrumb.setLevel(breadcrumb.getLevel()); | ||
rrwebBreadcrumb.setTimestamp(breadcrumb.getTimestamp().getTime()); | ||
rrwebBreadcrumb.setBreadcrumbTimestamp(breadcrumb.getTimestamp().getTime() / 1000.0); | ||
rrwebBreadcrumb.setBreadcrumbType("default"); | ||
return rrwebBreadcrumb; | ||
} | ||
|
||
RRWebEvent nativeBreadcrumb = super.convert(breadcrumb); | ||
|
||
// ignore native navigation breadcrumbs | ||
if (nativeBreadcrumb instanceof RRWebBreadcrumbEvent) { | ||
rrwebBreadcrumb = (RRWebBreadcrumbEvent) nativeBreadcrumb; | ||
if (rrwebBreadcrumb.getCategory() != null && rrwebBreadcrumb.getCategory().equals("navigation")) { | ||
return null; | ||
} | ||
} | ||
|
||
return nativeBreadcrumb; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
@interface RNSentryReplay : NSObject | ||
|
||
+ (void)updateOptions:(NSMutableDictionary *)options; | ||
|
||
+ (void)postInit; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
#import "RNSentryReplay.h" | ||
#import "RNSentryReplayBreadcrumbConverter.h" | ||
|
||
#if SENTRY_TARGET_REPLAY_SUPPORTED | ||
|
||
@implementation RNSentryReplay { | ||
} | ||
|
||
+ (void)updateOptions:(NSMutableDictionary *)options { | ||
NSDictionary *experiments = options[@"_experiments"]; | ||
[options removeObjectForKey:@"_experiments"]; | ||
if (experiments == nil) { | ||
NSLog(@"Session replay disabled via configuration"); | ||
return; | ||
} | ||
|
||
if (experiments[@"replaysSessionSampleRate"] == nil && | ||
experiments[@"replaysOnErrorSampleRate"] == nil) { | ||
NSLog(@"Session replay disabled via configuration"); | ||
return; | ||
} | ||
|
||
NSLog(@"Setting up session replay"); | ||
NSDictionary *replayOptions = options[@"mobileReplayOptions"] ?: @{}; | ||
|
||
[options setValue:@{ | ||
@"sessionReplay" : @{ | ||
@"sessionSampleRate" : experiments[@"replaysSessionSampleRate"] | ||
?: [NSNull null], | ||
@"errorSampleRate" : experiments[@"replaysOnErrorSampleRate"] | ||
?: [NSNull null], | ||
@"redactAllImages" : replayOptions[@"maskAllImages"] ?: [NSNull null], | ||
@"redactAllText" : replayOptions[@"maskAllText"] ?: [NSNull null], | ||
} | ||
} | ||
forKey:@"experimental"]; | ||
|
||
[RNSentryReplay addReplayRNRedactClasses:replayOptions]; | ||
} | ||
|
||
+ (void)addReplayRNRedactClasses:(NSDictionary *_Nullable)replayOptions { | ||
NSMutableArray *_Nonnull classesToRedact = [[NSMutableArray alloc] init]; | ||
if ([replayOptions[@"maskAllImages"] boolValue] == YES) { | ||
[classesToRedact addObject:NSClassFromString(@"RCTImageView")]; | ||
} | ||
if ([replayOptions[@"maskAllText"] boolValue] == YES) { | ||
[classesToRedact addObject:NSClassFromString(@"RCTTextView")]; | ||
} | ||
[PrivateSentrySDKOnly addReplayRedactClasses:classesToRedact]; | ||
} | ||
|
||
+ (void)postInit { | ||
RNSentryReplayBreadcrumbConverter *breadcrumbConverter = | ||
[[RNSentryReplayBreadcrumbConverter alloc] init]; | ||
[PrivateSentrySDKOnly configureSessionReplayWith:breadcrumbConverter | ||
screenshotProvider:nil]; | ||
} | ||
|
||
@end | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
@import Sentry; | ||
|
||
#if SENTRY_TARGET_REPLAY_SUPPORTED | ||
@class SentryRRWebEvent; | ||
|
||
@interface RNSentryReplayBreadcrumbConverter | ||
: NSObject <SentryReplayBreadcrumbConverter> | ||
|
||
- (instancetype _Nonnull)init; | ||
|
||
@end | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#import "RNSentryReplayBreadcrumbConverter.h" | ||
|
||
@import Sentry; | ||
|
||
#if SENTRY_TARGET_REPLAY_SUPPORTED | ||
|
||
@implementation RNSentryReplayBreadcrumbConverter { | ||
SentrySRDefaultBreadcrumbConverter *defaultConverter; | ||
} | ||
|
||
- (instancetype _Nonnull)init { | ||
if (self = [super init]) { | ||
self->defaultConverter = | ||
[SentrySessionReplayIntegration createDefaultBreadcrumbConverter]; | ||
} | ||
return self; | ||
} | ||
|
||
- (id<SentryRRWebEvent> _Nullable)convertFrom: | ||
(SentryBreadcrumb *_Nonnull)breadcrumb { | ||
assert(breadcrumb.timestamp != nil); | ||
|
||
if ([breadcrumb.category isEqualToString:@"touch"]) { | ||
NSMutableString *message; | ||
if (breadcrumb.data) { | ||
NSMutableArray *path = [breadcrumb.data valueForKey:@"path"]; | ||
if (path != nil) { | ||
message = [[NSMutableString alloc] init]; | ||
for (NSInteger i = MIN(3, [path count] - 1); i >= 0; i--) { | ||
NSDictionary *item = [path objectAtIndex:i]; | ||
[message appendString:[item objectForKey:@"name"]]; | ||
if ([item objectForKey:@"element"] || [item objectForKey:@"file"]) { | ||
[message appendString:@"("]; | ||
if ([item objectForKey:@"element"]) { | ||
[message appendString:[item objectForKey:@"element"]]; | ||
if ([item objectForKey:@"file"]) { | ||
[message appendString:@", "]; | ||
[message appendString:[item objectForKey:@"file"]]; | ||
} | ||
} else if ([item objectForKey:@"file"]) { | ||
[message appendString:[item objectForKey:@"file"]]; | ||
} | ||
[message appendString:@")"]; | ||
} | ||
if (i > 0) { | ||
[message appendString:@" > "]; | ||
} | ||
} | ||
} | ||
} | ||
return [SentrySessionReplayIntegration | ||
createBreadcrumbwithTimestamp:breadcrumb.timestamp | ||
category:@"ui.tap" | ||
message:message | ||
level:breadcrumb.level | ||
data:breadcrumb.data]; | ||
} else if ([breadcrumb.category isEqualToString:@"navigation"]) { | ||
return [SentrySessionReplayIntegration | ||
createBreadcrumbwithTimestamp:breadcrumb.timestamp | ||
category:breadcrumb.category | ||
message:nil | ||
level:breadcrumb.level | ||
data:breadcrumb.data]; | ||
} else { | ||
SentryRRWebEvent *nativeBreadcrumb = | ||
[self->defaultConverter convertFrom:breadcrumb]; | ||
|
||
// ignore native navigation breadcrumbs | ||
if (nativeBreadcrumb && nativeBreadcrumb.data && | ||
nativeBreadcrumb.data[@"payload"] && | ||
nativeBreadcrumb.data[@"payload"][@"category"] && | ||
[nativeBreadcrumb.data[@"payload"][@"category"] | ||
isEqualToString:@"navigation"]) { | ||
return nil; | ||
} | ||
return nativeBreadcrumb; | ||
} | ||
} | ||
|
||
@end | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters