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(performance): Add Time to Full Display and manual API for TTID #3654

Merged
merged 68 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
c6e994e
wip(ttid): Add ios frame tracker and android lifecycle hooks
krystofwoldrich Feb 8, 2024
8d457b5
Add android ttid fragment tracing
krystofwoldrich Feb 12, 2024
70ab19b
Merge remote-tracking branch 'origin/main' into @krystofwoldrich/add-…
krystofwoldrich Feb 14, 2024
4338dfe
chore: update scripts/update-cocoa.sh to 8.20.0
web-flow Feb 14, 2024
f070d7c
Merge branch 'main' into @krystofwoldrich/add-ttid
krystofwoldrich Feb 15, 2024
e2d2e4f
Merge remote-tracking branch 'origin/deps/scripts/update-cocoa.sh' in…
krystofwoldrich Feb 15, 2024
c0b1480
Clean up ios implementation and use event emitters
krystofwoldrich Feb 15, 2024
66cd5b2
Add TTID JS interface and JS/iOS impl
krystofwoldrich Feb 15, 2024
20e6513
Make sentry event emitter addListener synchronous
krystofwoldrich Feb 16, 2024
06ee5e0
Use RNSentry prefix in android impl
krystofwoldrich Feb 16, 2024
5329a1e
fix lint
krystofwoldrich Feb 19, 2024
3dc6faa
fix sentry event listener once
krystofwoldrich Feb 19, 2024
1a91804
Cleanup java impl fragments initial frame tracking
krystofwoldrich Feb 19, 2024
db31c23
Merge branch 'main' into @krystofwoldrich/add-ttid
krystofwoldrich Feb 19, 2024
e1e06dc
fix android native test
krystofwoldrich Feb 19, 2024
3dbcab1
Remove dependency on rn screens on android
krystofwoldrich Feb 20, 2024
970d9d1
tmp
krystofwoldrich Feb 21, 2024
24f5d31
fix java test
krystofwoldrich Feb 21, 2024
de56364
add unit tests job
krystofwoldrich Feb 21, 2024
1e21374
add more tests
krystofwoldrich Feb 21, 2024
3c98d05
Add react navigation ttid test skeleton
krystofwoldrich Feb 21, 2024
49f09a3
return device tests
krystofwoldrich Feb 21, 2024
1984477
fix(expo): Ensure authToken is not written to application package
krystofwoldrich Feb 26, 2024
e386b9c
finish js integration tests
krystofwoldrich Feb 26, 2024
9f4c4db
clean up navigation processing span
krystofwoldrich Feb 26, 2024
cfcec74
Add native init to the instrumentation class, add native ios tests
krystofwoldrich Feb 26, 2024
b6137a9
fix native tests
krystofwoldrich Feb 26, 2024
a5e55c5
Remove misc header
krystofwoldrich Feb 27, 2024
ce1f310
clean up tester app build gradle
krystofwoldrich Feb 27, 2024
cb5afc6
Cleanup android impl
krystofwoldrich Feb 27, 2024
a63048c
return trace context interface
krystofwoldrich Feb 27, 2024
12e59f3
Revert "fix(expo): Ensure authToken is not written to application pac…
krystofwoldrich Feb 27, 2024
f6c650f
clean up sample rn app
krystofwoldrich Feb 27, 2024
5577b1e
add sentry emitter jsdoc
krystofwoldrich Feb 27, 2024
6b2b486
fix js tests
krystofwoldrich Feb 27, 2024
bb77a07
Add sentry event emitter tests
krystofwoldrich Feb 27, 2024
e3c4278
add ttid enabled flag
krystofwoldrich Feb 27, 2024
8c55c0a
fix lint sample
krystofwoldrich Feb 27, 2024
720e3ea
fix rnsscreen swizzle on new architecture
krystofwoldrich Feb 27, 2024
2cc69c1
ensure swizzle runs on main thread
krystofwoldrich Feb 27, 2024
8f50760
keep RNSentry headers private
krystofwoldrich Feb 27, 2024
7305730
Remove sentry log as it's not public
krystofwoldrich Feb 28, 2024
877af8a
fix ios test project
krystofwoldrich Feb 28, 2024
1c9a12b
auto ttid disabled by default, add disabled tests
krystofwoldrich Feb 28, 2024
69a40d0
fix prettier
krystofwoldrich Feb 28, 2024
dfadf15
Add changelog
krystofwoldrich Feb 28, 2024
6074faa
fix app start ttid measurement
krystofwoldrich Feb 28, 2024
8981840
fix back navigation, do not create initial display
krystofwoldrich Mar 4, 2024
5e30fc1
feat(performance): Add Time to Full Display and manual API for TTID
krystofwoldrich Mar 4, 2024
3ea527e
add time to full display JS api
krystofwoldrich Mar 5, 2024
10191ce
review fixes
krystofwoldrich Mar 5, 2024
761da45
Merge branch '@krystofwoldrich/add-ttid' into @krystofwoldrich/add-tt…
krystofwoldrich Mar 6, 2024
0e22f23
Finish iOS impl and add Android (both legacy arch)
krystofwoldrich Mar 7, 2024
58e76d5
Merge remote-tracking branch 'origin/main' into @krystofwoldrich/add-…
krystofwoldrich Mar 7, 2024
9b833b2
Fix sample lint, remove duplicate native ios test
krystofwoldrich Mar 7, 2024
724f9b8
Merge branch '@krystofwoldrich/add-ttid' into @krystofwoldrich/add-tt…
krystofwoldrich Mar 7, 2024
a51e36e
Add unit tests with test client
krystofwoldrich Mar 8, 2024
77db6f4
Add manual API tests
krystofwoldrich Mar 12, 2024
b70ce11
Make manual api more friend for combination with auto instrumentation
krystofwoldrich Mar 12, 2024
fa0ec77
fix lint
krystofwoldrich Mar 12, 2024
2a7f4ae
remove unused code
krystofwoldrich Mar 12, 2024
f6fbc7e
fix sample apps
krystofwoldrich Mar 12, 2024
395a14b
add changelog
krystofwoldrich Mar 12, 2024
c06e638
Fix full display can never be before initial display
krystofwoldrich Mar 15, 2024
da7468d
fix android review comments
krystofwoldrich Mar 15, 2024
1edeb6d
Merge branch 'main' into @krystofwoldrich/add-ttid
krystofwoldrich Mar 18, 2024
8639217
Merge branch '@krystofwoldrich/add-ttid' into @krystofwoldrich/add-tt…
krystofwoldrich Mar 18, 2024
cbc51bf
Merge remote-tracking branch 'origin/main' into @krystofwoldrich/add-…
krystofwoldrich Mar 18, 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
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Features

- Add automatic tracing of time to initial display for `react-navigation` ([#3588](https://github.com/getsentry/sentry-react-native/pull/3588))
- Automatic tracing of time to initial display for `react-navigation` ([#3588](https://github.com/getsentry/sentry-react-native/pull/3588))

When enabled the instrumentation will create TTID spans and measurements.
The TTID timestamp represent moment when the `react-navigation` screen
Expand All @@ -20,6 +20,25 @@
});
```

- Tracing of full display using manual API ([#3654](https://github.com/getsentry/sentry-react-native/pull/3654))

In combination with the `react-navigation` automatic instrumentation you can record when
the application screen is fully rendered.

For more examples and manual time to initial display see [the documentation](https://docs.sentry.io/platforms/react-native/performance/instrumentation/time-to-display).

```javascript
function Example() {
const [loaded] = React.useState(false);

return <View>
<Sentry.TimeToFullDisplay record={loaded}>
<Text>Example content</Text>
</Sentry.TimeToFullDisplay>
</View>;
}
```

### Fixes

- Allow custom `sentryUrl` for Expo updates source maps uploads ([#3664](https://github.com/getsentry/sentry-react-native/pull/3664))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package io.sentry.react;

import android.app.Activity;
import android.content.Context;
import android.view.View;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Map;

import io.sentry.SentryDate;
import io.sentry.SentryDateProvider;
import io.sentry.android.core.AndroidLogger;
import io.sentry.android.core.BuildInfoProvider;
import io.sentry.android.core.SentryAndroidDateProvider;
import io.sentry.android.core.internal.util.FirstDrawDoneListener;

public class RNSentryOnDrawReporterManager extends SimpleViewManager<RNSentryOnDrawReporterManager.RNSentryOnDrawReporterView> {

public static final String REACT_CLASS = "RNSentryOnDrawReporter";
private final @NotNull ReactApplicationContext mCallerContext;

public RNSentryOnDrawReporterManager(ReactApplicationContext reactContext) {
mCallerContext = reactContext;
}

@NotNull
@Override
public String getName() {
return REACT_CLASS;
}

@NotNull
@Override
protected RNSentryOnDrawReporterView createViewInstance(@NotNull ThemedReactContext themedReactContext) {
return new RNSentryOnDrawReporterView(mCallerContext, new BuildInfoProvider(new AndroidLogger()));
}

@ReactProp(name = "initialDisplay", defaultBoolean = false)
public void setInitialDisplay(RNSentryOnDrawReporterView view, boolean initialDisplay) {
view.setInitialDisplay(initialDisplay);
}

@ReactProp(name = "fullDisplay", defaultBoolean = false)
public void setFullDisplay(RNSentryOnDrawReporterView view, boolean fullDisplay) {
view.setFullDisplay(fullDisplay);
}

public Map getExportedCustomBubblingEventTypeConstants() {
return MapBuilder.builder().put(
"onDrawNextFrameView",
MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of("bubbled", "onDrawNextFrame")
)
).build();
}

public static class RNSentryOnDrawReporterView extends View {

private final @Nullable ReactApplicationContext reactContext;
private final @NotNull SentryDateProvider dateProvider = new SentryAndroidDateProvider();
private final @Nullable Runnable emitInitialDisplayEvent;
private final @Nullable Runnable emitFullDisplayEvent;
private final @Nullable BuildInfoProvider buildInfo;


public RNSentryOnDrawReporterView(@NotNull Context context) {
super(context);
reactContext = null;
buildInfo = null;
emitInitialDisplayEvent = null;
emitFullDisplayEvent = null;
}

public RNSentryOnDrawReporterView(@NotNull ReactApplicationContext context, @NotNull BuildInfoProvider buildInfoProvider) {
super(context);
reactContext = context;
buildInfo = buildInfoProvider;
emitInitialDisplayEvent = () -> emitDisplayEvent("initialDisplay");
emitFullDisplayEvent = () -> emitDisplayEvent("fullDisplay");
}

public void setFullDisplay(boolean fullDisplay) {
if (!fullDisplay) {
return;
}

registerForNextDraw(emitFullDisplayEvent);
}

public void setInitialDisplay(boolean initialDisplay) {
if (!initialDisplay) {
return;
}

registerForNextDraw(emitInitialDisplayEvent);
}

private void registerForNextDraw(@Nullable Runnable emitter) {
if (reactContext == null) {
return;
}

@Nullable Activity activity = reactContext.getCurrentActivity();
if (activity == null || emitter == null || buildInfo == null) {
return;
}

FirstDrawDoneListener
.registerForNextDraw(activity, emitter, buildInfo);
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved
}

private void emitDisplayEvent(String type) {
final SentryDate endDate = dateProvider.now();

WritableMap event = Arguments.createMap();
event.putString("type", type);
event.putDouble("newFrameTimestampInSeconds", endDate.nanoTimestamp() / 1e9);

if (reactContext == null) {
return;
}
reactContext
.getJSModule(RCTEventEmitter.class)
.receiveEvent(getId(), "onDrawNextFrameView", event);
}
}
}
13 changes: 13 additions & 0 deletions android/src/main/java/io/sentry/react/RNSentryPackage.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package io.sentry.react;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.uimanager.ViewManager;

public class RNSentryPackage extends TurboReactPackage {

Expand Down Expand Up @@ -43,4 +47,13 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() {
};
}

@NonNull
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.asList(
new RNSentryOnDrawReporterManager(reactContext)
);
}

}
2 changes: 1 addition & 1 deletion ios/RNSentryFramesTrackerListener.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ - (void)framesTrackerHasNewFrame:(NSDate *)newFrameDate
{
[_framesTracker removeListener:self];
NSNumber *newFrameTimestampInSeconds = [NSNumber numberWithDouble:[newFrameDate timeIntervalSince1970]];

if (_emitNewFrameEvent) {
_emitNewFrameEvent(newFrameTimestampInSeconds);
}
Expand Down
70 changes: 70 additions & 0 deletions ios/RNSentryOnDrawReporter.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#import <UIKit/UIKit.h>
#import <React/RCTViewManager.h>
#import "RNSentryFramesTrackerListener.h"
#import <Sentry/SentryDependencyContainer.h>

@interface RNSentryOnDrawReporter : RCTViewManager

@end

@interface RNSentryOnDrawReporterView : UIView

@property (nonatomic, strong) RNSentryFramesTrackerListener* framesListener;
@property (nonatomic, copy) RCTBubblingEventBlock onDrawNextFrame;
@property (nonatomic) bool fullDisplay;
@property (nonatomic) bool initialDisplay;
@property (nonatomic, weak) RNSentryOnDrawReporter* delegate;

@end

@implementation RNSentryOnDrawReporter

RCT_EXPORT_MODULE(RNSentryOnDrawReporter)
RCT_EXPORT_VIEW_PROPERTY(onDrawNextFrame, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(initialDisplay, BOOL)
RCT_EXPORT_VIEW_PROPERTY(fullDisplay, BOOL)

- (UIView *)view
{
RNSentryOnDrawReporterView* view = [[RNSentryOnDrawReporterView alloc] init];
return view;
}

@end

@implementation RNSentryOnDrawReporterView

- (instancetype)init {
self = [super init];
if (self) {
RNSentryEmitNewFrameEvent emitNewFrameEvent = ^(NSNumber *newFrameTimestampInSeconds) {
if (self->_fullDisplay) {
self.onDrawNextFrame(@{
@"newFrameTimestampInSeconds": newFrameTimestampInSeconds,
@"type": @"fullDisplay"
});
return;
}

if (self->_initialDisplay) {
self.onDrawNextFrame(@{
@"newFrameTimestampInSeconds": newFrameTimestampInSeconds,
@"type": @"initialDisplay"
});
return;
}
};
_framesListener = [[RNSentryFramesTrackerListener alloc] initWithSentryFramesTracker:[[SentryDependencyContainer sharedInstance] framesTracker]
andEventEmitter:emitNewFrameEvent];
}
return self;
}

- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
if (_fullDisplay || _initialDisplay) {
[_framesListener startListening];
}
}

@end
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"@sentry/wizard": "3.16.3",
"@types/jest": "^29.5.3",
"@types/node": "^20.9.3",
"@types/react": "^18.2.14",
"@types/react": "^18.2.64",
"@types/uglify-js": "^3.17.2",
"@types/uuid": "^9.0.4",
"@types/xmlhttprequest": "^1.8.2",
Expand All @@ -106,6 +106,7 @@
"prettier": "^2.0.5",
"react": "18.2.0",
"react-native": "0.73.2",
"react-test-renderer": "^18.2.0",
"replace-in-file": "^7.0.1",
"rimraf": "^4.1.1",
"ts-jest": "^29.1.1",
Expand Down
1 change: 1 addition & 0 deletions samples/expo/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export default function TabOneScreen() {

return (
<View style={styles.container}>
<Sentry.TimeToInitialDisplay record />
<Text>Welcome to Sentry Expo Sample App!</Text>
<Button
title="Capture message"
Expand Down
3 changes: 2 additions & 1 deletion samples/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@react-navigation/native": "^6.1.9",
"@react-navigation/native-stack": "^6.9.17",
"@react-navigation/stack": "^6.3.20",
"delay": "^6.0.0",
"react": "18.2.0",
"react-native": "0.73.2",
"react-native-gesture-handler": "^2.14.0",
Expand All @@ -38,7 +39,7 @@
"@react-native/eslint-config": "^0.73.1",
"@react-native/metro-config": "^0.73.1",
"@react-native/typescript-config": "^0.73.1",
"@types/react": "^18.2.13",
"@types/react": "^18.2.65",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.37.0",
Expand Down
Loading
Loading