Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Unreleased

- feat: Add `wrap` wrapper method with profiler and touch event boundary #1728
- feat: App-start measurements, if using the `wrap` wrapper, will now finish on the root component mount #1728

## 3.0.0-beta.2

- feat: Native slow/frozen frames measurements #1711
Expand Down
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,19 @@
"react-native": ">=0.56.0"
},
"dependencies": {
"@sentry/browser": "6.7.1",
"@sentry/core": "6.7.1",
"@sentry/hub": "6.7.1",
"@sentry/integrations": "6.7.1",
"@sentry/react": "6.7.1",
"@sentry/tracing": "6.7.1",
"@sentry/types": "6.7.1",
"@sentry/utils": "6.7.1",
"@sentry/browser": "6.12.0-beta.2",
Copy link
Member

Choose a reason for hiding this comment

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

Let’s not bump to the beta please.

Copy link
Member

Choose a reason for hiding this comment

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

We won't if you release it before Tuesday next week :)

Copy link
Member

Choose a reason for hiding this comment

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

We'll make it happen :)

Copy link
Member

Choose a reason for hiding this comment

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

Thanks!
We can merge this and ship another beta still today if there's time.

Next week bump deps to GA and GA this too

"@sentry/core": "6.12.0-beta.2",
"@sentry/hub": "6.12.0-beta.2",
"@sentry/integrations": "6.12.0-beta.2",
"@sentry/react": "6.12.0-beta.2",
"@sentry/tracing": "6.12.0-beta.2",
"@sentry/types": "6.12.0-beta.2",
"@sentry/utils": "6.12.0-beta.2",
"@sentry/wizard": "^1.2.2"
},
"devDependencies": {
"@sentry-internal/eslint-config-sdk": "6.7.1",
"@sentry-internal/eslint-plugin-sdk": "6.7.1",
"@sentry-internal/eslint-config-sdk": "6.12.0-beta.2",
"@sentry-internal/eslint-plugin-sdk": "6.12.0-beta.2",
"@sentry/typescript": "^5.20.0",
"@types/jest": "^26.0.15",
"@types/react": "^16.9.49",
Expand Down
56 changes: 23 additions & 33 deletions sample/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const reactNavigationV5Instrumentation = new Sentry.ReactNavigationV5Instrumenta
routeChangeTimeoutMs: 500, // How long it will wait for the route change to complete. Default is 1000ms
},
);

Sentry.init({
// Replace the example DSN below with your own DSN:
dsn: SENTRY_INTERNAL_DSN,
Expand Down Expand Up @@ -71,38 +70,29 @@ const App = () => {
const navigation = React.useRef<NavigationContainerRef>();

return (
<Sentry.Profiler name="App">
<Provider store={store}>
<NavigationContainer
ref={navigation}
onReady={() => {
reactNavigationV5Instrumentation.registerNavigationContainer(
navigation,
);
}}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Tracker" component={TrackerScreen} />
<Stack.Screen
name="ManualTracker"
component={ManualTrackerScreen}
/>
<Stack.Screen
name="PerformanceTiming"
component={PerformanceTimingScreen}
/>
<Stack.Screen name="Redux" component={ReduxScreen} />
<Stack.Screen
name="EndToEndTests"
component={EndToEndTestsScreen}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>
</Sentry.Profiler>
<Provider store={store}>
<NavigationContainer
ref={navigation}
onReady={() => {
reactNavigationV5Instrumentation.registerNavigationContainer(
navigation,
);
}}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Tracker" component={TrackerScreen} />
<Stack.Screen name="ManualTracker" component={ManualTrackerScreen} />
<Stack.Screen
name="PerformanceTiming"
component={PerformanceTimingScreen}
/>
<Stack.Screen name="Redux" component={ReduxScreen} />
<Stack.Screen name="EndToEndTests" component={EndToEndTestsScreen} />
</Stack.Navigator>
</NavigationContainer>
</Provider>
);
};

export default Sentry.withTouchEventBoundary(App, {
ignoreNames: ['Provider', 'UselessName', /^SomeRegex/],
});
// Wrap your app to get more features out of the box such as auto performance monitoring.
export default Sentry.wrap(App);
14 changes: 12 additions & 2 deletions src/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,18 @@ import { SDK_NAME, SDK_VERSION } from "./version";
export { ReactNativeBackend } from "./backend";
export { ReactNativeOptions } from "./options";
export { ReactNativeClient } from "./client";
// eslint-disable-next-line deprecation/deprecation
export { init, setDist, setRelease, nativeCrash, flush, close } from "./sdk";

export {
init,
wrap,
// eslint-disable-next-line deprecation/deprecation
setDist,
// eslint-disable-next-line deprecation/deprecation
setRelease,
nativeCrash,
flush,
close,
} from "./sdk";
export { TouchEventBoundary, withTouchEventBoundary } from "./touchevents";

export {
Expand Down
1 change: 0 additions & 1 deletion src/js/integrations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ export { DebugSymbolicator } from "./debugsymbolicator";
export { DeviceContext } from "./devicecontext";
export { ReactNativeErrorHandlers } from "./reactnativeerrorhandlers";
export { Release } from "./release";
export { StallTracking } from "./stalltracking";
38 changes: 8 additions & 30 deletions src/js/measurements.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { getCurrentHub, getMainCarrier, Hub } from "@sentry/hub";
import { Transaction } from "@sentry/tracing";
import { CustomSamplingContext, TransactionContext } from "@sentry/types";
import { logger } from "@sentry/utils";

import { StallTracking } from "./integrations";
import { ReactNativeTracing } from "./tracing";

/**
Expand Down Expand Up @@ -58,40 +56,20 @@ const _patchStartTransaction = (
const reactNativeTracing = getCurrentHub().getIntegration(
ReactNativeTracing
);
const stallTracking = getCurrentHub().getIntegration(StallTracking);

if (reactNativeTracing) {
reactNativeTracing.onTransactionStart(transaction);
}

if (stallTracking) {
if (!transactionContext.startTimestamp) {
const finishStallTracking = stallTracking.registerTransactionStart(
transaction
);

// eslint-disable-next-line @typescript-eslint/unbound-method
const originalFinish = transaction.finish;

transaction.finish = (endTimestamp: number | undefined) => {
const stallMeasurements = finishStallTracking();

// Sometimes the measurements will not be tracked due to some underlying reason, we don't add them in that case.
if (stallMeasurements) {
transaction.setMeasurements(stallMeasurements);
}
// eslint-disable-next-line @typescript-eslint/unbound-method
const originalFinish = transaction.finish;

if (reactNativeTracing) {
reactNativeTracing.onTransactionFinish(transaction);
}
transaction.finish = (endTimestamp: number | undefined) => {
if (reactNativeTracing) {
reactNativeTracing.onTransactionFinish(transaction);
}

return originalFinish.apply(transaction, [endTimestamp]);
};
} else {
logger.log(
"[StallTracking] Stalls will not be tracked due to `startTimestamp` being set."
);
}
return originalFinish.apply(transaction, [endTimestamp]);
};
}

return transaction;
Expand Down
14 changes: 11 additions & 3 deletions src/js/options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { BrowserOptions } from "@sentry/react";
import { ProfilerProps } from "@sentry/react/dist/profiler";

import { TouchEventBoundaryProps } from "./touchevents";

/**
* Configuration options for the Sentry ReactNative SDK.
Expand Down Expand Up @@ -66,9 +69,14 @@ export interface ReactNativeOptions extends BrowserOptions {
didCallNativeInit: boolean;
}) => void;

/** Enable JS event loop stall tracking. Enabled by default. */
enableStallTracking?: boolean;

/** Enable auto performance tracking by default. */
enableAutoPerformanceTracking?: boolean;
}

export interface ReactNativeWrapperOptions {
/** Props for the root React profiler */
profilerProps?: ProfilerProps;

/** Props for the root touch event boundary */
touchEventBoundaryProps?: TouchEventBoundaryProps;
}
44 changes: 35 additions & 9 deletions src/js/sdk.ts → src/js/sdk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import { RewriteFrames } from "@sentry/integrations";
import { defaultIntegrations, getCurrentHub } from "@sentry/react";
import { StackFrame } from "@sentry/types";
import { getGlobalObject, logger } from "@sentry/utils";
import * as React from "react";

import { ReactNativeClient } from "./client";
import {
DebugSymbolicator,
DeviceContext,
ReactNativeErrorHandlers,
Release,
StallTracking,
} from "./integrations";
import { ReactNativeOptions } from "./options";
import { ReactNativeOptions, ReactNativeWrapperOptions } from "./options";
import { ReactNativeScope } from "./scope";
import { ReactNativeTracing } from "./tracing";
import { TouchEventBoundary } from "./touchevents";
import { ReactNativeProfiler, ReactNativeTracing } from "./tracing";

const IGNORED_DEFAULT_INTEGRATIONS = [
"GlobalHandlers", // We will use the react-native internal handlers
Expand All @@ -26,12 +27,11 @@ const DEFAULT_OPTIONS: ReactNativeOptions = {
enableNativeCrashHandling: true,
enableNativeNagger: true,
autoInitializeNativeSdk: true,
enableStallTracking: true,
enableAutoPerformanceTracking: true,
};

/**
* Inits the SDK
* Inits the SDK and returns the final options.
*/
export function init(passedOptions: ReactNativeOptions): void {
const reactNativeHub = new Hub(undefined, new ReactNativeScope());
Expand Down Expand Up @@ -89,10 +89,6 @@ export function init(passedOptions: ReactNativeOptions): void {
if (tracingEnabled) {
if (options.enableAutoPerformanceTracking) {
options.defaultIntegrations.push(new ReactNativeTracing());

if (options.enableStallTracking) {
options.defaultIntegrations.push(new StallTracking());
}
}
}
}
Expand All @@ -108,6 +104,36 @@ export function init(passedOptions: ReactNativeOptions): void {
}
}

/**
* Inits the Sentry React Native SDK with automatic instrumentation and wrapped features.
*/
export function wrap<P>(
RootComponent: React.ComponentType<P>,
options?: ReactNativeWrapperOptions
): React.ComponentType<P> {
const tracingIntegration = getCurrentHub().getIntegration(ReactNativeTracing);
if (tracingIntegration) {
tracingIntegration.useAppStartWithProfiler = true;
}

const profilerProps = {
...(options?.profilerProps ?? {}),
name: RootComponent.displayName ?? "Root",
};

const RootApp: React.FC<P> = (appProps) => {
return (
<TouchEventBoundary {...(options?.touchEventBoundaryProps ?? {})}>
<ReactNativeProfiler {...profilerProps}>
<RootComponent {...appProps} />
</ReactNativeProfiler>
</TouchEventBoundary>
);
};

return RootApp;
}

/**
* Deprecated. Sets the release on the event.
* NOTE: Does not set the release on sessions.
Expand Down
1 change: 1 addition & 0 deletions src/js/tracing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export {
ReactNavigationRoute,
ReactNavigationTransactionContext,
} from "./types";
export { ReactNativeProfiler } from "./reactnativeprofiler";
26 changes: 26 additions & 0 deletions src/js/tracing/reactnativeprofiler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getCurrentHub, Profiler } from "@sentry/react";

import { ReactNativeTracing } from "./reactnativetracing";

/**
* Custom profiler for the React Native app root.
*/
export class ReactNativeProfiler extends Profiler {
/**
* Get the app root mount time.
*/
public componentDidMount(): void {
super.componentDidMount();

const tracingIntegration = getCurrentHub().getIntegration(
ReactNativeTracing
);

if (this._mountSpan && tracingIntegration) {
if (typeof this._mountSpan.endTimestamp !== "undefined") {
// The first root component mount is the app start finish.
tracingIntegration.onAppStartFinish(this._mountSpan.endTimestamp);
}
}
}
}
Loading