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

feat(replay): Add breadcrumbs mapping from RN to RRWeb format #3846

Merged
merged 25 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
66b14d8
touch events on android
vaind May 31, 2024
a9139c6
fix touchevent tests
vaind Jun 3, 2024
fb1bb90
Update android/src/main/java/io/sentry/react/RNSentryReplayBreadcrumb…
vaind Jun 4, 2024
e725aa9
ios breadcrumb converter
vaind Jun 5, 2024
d5c8e67
delegate to default breadcrumb converter
vaind Jun 10, 2024
3d76fae
fix: module access
vaind Jun 11, 2024
9092169
Merge branch 'feat/replay' into feat/replay-breadcrumbs
vaind Jun 18, 2024
b4a1d8f
move replay stuff to RNSentrySessionReplay
vaind Jun 19, 2024
2ebf638
move setBreadcrumbConverter to init
vaind Jun 19, 2024
0f17b72
try to fix CI
vaind Jun 19, 2024
ae558fe
navigation breadcrumbs
vaind Jun 19, 2024
04c5c9d
try to fix CI
vaind Jun 19, 2024
6211bbf
pin xcode to 15.4 for e2e and sample app builds
krystofwoldrich Jun 20, 2024
96c7aab
chore: update cocoa breadcrumb converter to match latest changes in t…
vaind Jun 24, 2024
1a13827
more updates based on upstream changes
vaind Jun 24, 2024
5564c88
Revert "pin xcode to 15.4 for e2e and sample app builds"
krystofwoldrich Jun 25, 2024
8d4f937
review changes
vaind Jun 25, 2024
272b4bb
fix changelog
vaind Jun 25, 2024
0a95f72
Merge branch 'feat/replay' into feat/replay-breadcrumbs
vaind Jun 26, 2024
30ed290
cleanup TODOs
vaind Jun 26, 2024
f71fb66
touch event path for replay breadcrumbs
vaind Jun 26, 2024
2f2ff79
remove native navigation breadcrumbs on iOS
vaind Jun 26, 2024
dcc9753
renames
vaind Jun 26, 2024
a138db1
ignore native navigation breadcrumbs on android
vaind Jun 26, 2024
e3aaee8
fix android
vaind Jun 26, 2024
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
2 changes: 1 addition & 1 deletion RNSentry.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ is_new_arch_enabled = ENV["RCT_NEW_ARCH_ENABLED"] == "1"
is_using_hermes = (ENV['USE_HERMES'] == nil && is_hermes_default) || ENV['USE_HERMES'] == '1'
new_arch_enabled_flag = (is_new_arch_enabled ? folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED" : "")
sentry_profiling_supported_flag = (is_profiling_supported ? " -DSENTRY_PROFILING_SUPPORTED=1" : "")
other_cflags = "$(inherited)" + new_arch_enabled_flag + sentry_profiling_supported_flag
other_cflags = "$(inherited) -fcxx-modules -fmodules" + new_arch_enabled_flag + sentry_profiling_supported_flag
vaind marked this conversation as resolved.
Show resolved Hide resolved

Pod::Spec.new do |s|
s.name = 'RNSentry'
Expand Down
3 changes: 3 additions & 0 deletions android/src/main/java/io/sentry/react/RNSentryModuleImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ public void initNativeSdk(final ReadableMap rnOptions, Promise promise) {
}
});

Sentry.getCurrentHub().getOptions().getReplayController()
.setBreadcrumbConverter(new RNSentryReplayBreadcrumbConverter());

vaind marked this conversation as resolved.
Show resolved Hide resolved
promise.resolve(true);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 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;

vaind marked this conversation as resolved.
Show resolved Hide resolved
if (breadcrumb.getCategory().equals("touch")) {
rrwebBreadcrumb.setCategory("ui.tap");
Object target = breadcrumb.getData("target");
if (target != null) {
rrwebBreadcrumb.setMessage(target.toString());
}
rrwebBreadcrumb.setData(breadcrumb.getData());
}

if (rrwebBreadcrumb.getCategory() != null && !rrwebBreadcrumb.getCategory().isEmpty()) {
rrwebBreadcrumb.setTimestamp(breadcrumb.getTimestamp().getTime());
rrwebBreadcrumb.setBreadcrumbTimestamp(breadcrumb.getTimestamp().getTime() / 1000.0);
rrwebBreadcrumb.setBreadcrumbType("default");
return rrwebBreadcrumb;
}

return super.convert(breadcrumb);
}
}
4 changes: 4 additions & 0 deletions ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#import "RNSentryEvents.h"
#import "RNSentryDependencyContainer.h"
#import "RNSentryBreadcrumbConverter.h"

#if SENTRY_HAS_UIKIT
#import "RNSentryRNSScreen.h"
Expand Down Expand Up @@ -90,6 +91,9 @@ + (BOOL)requiresMainQueueSetup {

[SentrySDK startWithOptions:sentryOptions];

RNSentryBreadcrumbConverter* breadcrumbConverter = [[RNSentryBreadcrumbConverter alloc] init];
[PrivateSentrySDKOnly configureSessionReplayWith: breadcrumbConverter screenshotProvider: nil];

#if TARGET_OS_IPHONE || TARGET_OS_MACCATALYST
BOOL appIsActive = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
#else
Expand Down
13 changes: 13 additions & 0 deletions ios/RNSentryBreadcrumbConverter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@import Sentry;

@class SentryRRWebEvent;

@interface RNSentryBreadcrumbConverter
: NSObject <SentryReplayBreadcrumbConverter>

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios dev dynamic-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios dev dynamic-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios production dynamic-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios production dynamic-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy macos dev no-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy macos dev no-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy macos production no-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy macos production no-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios production no-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios production no-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build new ios dev no-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build new ios dev no-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios dev no-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build legacy ios dev no-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build new ios production no-frameworks

type argument 'SentryReplayBreadcrumbConverter' must be a pointer (requires a '*')

Check failure on line 6 in ios/RNSentryBreadcrumbConverter.h

View workflow job for this annotation

GitHub Actions / Build new ios production no-frameworks

type arguments cannot be applied to non-parameterized class 'NSObject'
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved

- (NSArray<SentryRRWebEvent *> *_Nonnull)
convertWithBreadcrumbs:(NSArray<SentryBreadcrumb *> *_Nonnull)breadcrumbs
from:(NSDate *_Nonnull)from
until:(NSDate *_Nonnull)until;

@end
32 changes: 32 additions & 0 deletions ios/RNSentryBreadcrumbConverter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#import "RNSentryBreadcrumbConverter.h"

@implementation RNSentryBreadcrumbConverter {
}

- (NSArray<SentryRRWebEvent *> *_Nonnull)
convertWithBreadcrumbs:(NSArray<SentryBreadcrumb *> *_Nonnull)breadcrumbs
from:(NSDate *_Nonnull)from
until:(NSDate *_Nonnull)until {
NSMutableArray<SentryRRWebEvent *> *outBreadcrumbs = [NSMutableArray array];
for (SentryBreadcrumb *breadcrumb in breadcrumbs) {
if (breadcrumb.timestamp &&
[breadcrumb.timestamp compare:from] != NSOrderedAscending &&
[breadcrumb.timestamp compare:until] != NSOrderedDescending) {
if ([breadcrumb.category isEqualToString:@"touch"]) {
SentryRRWebBreadcrumbEvent *rrwebBreadcrumb =
[[SentryRRWebBreadcrumbEvent alloc]
initWithTimestamp:breadcrumb.timestamp
category:@"ui.tap"
message:breadcrumb.data ? [breadcrumb.data valueForKey:@"target"] : nil
level:breadcrumb.level
data:breadcrumb.data];
[outBreadcrumbs addObject:rrwebBreadcrumb];
} else {
// TODO delegate to the default breadcrumb converter
vaind marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
return outBreadcrumbs;
}

@end
5 changes: 4 additions & 1 deletion src/js/touchevents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ class TouchEventBoundary extends React.Component<TouchEventBoundaryProps> {
const level = 'info' as SeverityLevel;
const crumb = {
category: this.props.breadcrumbCategory,
data: { componentTree: componentTreeNames },
data: {
componentTree: componentTreeNames,
...(activeLabel && { target: activeLabel }),
},
level: level,
message: activeLabel
? `Touch event within element: ${activeLabel}`
Expand Down
3 changes: 3 additions & 0 deletions test/touchevents.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ describe('TouchEventBoundary._onTouchStart', () => {
category: defaultProps.breadcrumbCategory,
data: {
componentTree: ['View', 'Connect(View)', 'LABEL!'],
target: "LABEL!",
},
level: 'info' as SeverityLevel,
message: 'Touch event within element: LABEL!',
Expand Down Expand Up @@ -161,6 +162,7 @@ describe('TouchEventBoundary._onTouchStart', () => {
category: defaultProps.breadcrumbCategory,
data: {
componentTree: ['Styled(View2)', 'Styled(View)'],
target: 'Styled(View2)',
},
level: 'info' as SeverityLevel,
message: 'Touch event within element: Styled(View2)',
Expand Down Expand Up @@ -211,6 +213,7 @@ describe('TouchEventBoundary._onTouchStart', () => {
category: defaultProps.breadcrumbCategory,
data: {
componentTree: ['Connect(View)', 'Styled(View)'],
target: 'Connect(View)',
},
level: 'info' as SeverityLevel,
message: 'Touch event within element: Connect(View)',
Expand Down
Loading