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

misc(deprecations): Add Tracing deprecations and replacements #4073

Merged
merged 6 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## Unreleased

### Changes

- React Native Tracing Deprecations ([#4073](https://github.com/getsentry/sentry-react-native/pull/4073))
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved
- `new ReactNativeTracing` to `reactNativeTracingIntegration()`
- `new ReactNavigationInstrumentation` to `reactNativeTracingIntegration()`.
- `new ReactNativeNavigationInstrumentation` to `reactNativeTracingIntegration()`.
- `ReactNavigationV4Instrumentation` won't be supported in the next major SDK version, upgrade to `react-navigation@5` or newer.
- `RoutingInstrumentation` and `RoutingInstrumentationInstance` replace by `Integration` interface from `@sentry/types`.
- `enableAppStartTracking`, `enableNativeFramesTracking`, `enableStallTracking`, `enableUserInteractionTracing` moved to `Sentry.init({})` root options.

## 5.31.1

### Fixes
Expand Down
4 changes: 2 additions & 2 deletions samples/expo/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ LogBox.ignoreAllLogs();
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();

const routingInstrumentation = new Sentry.ReactNavigationInstrumentation({
const routingInstrumentation = Sentry.reactNavigationIntegration({
enableTimeToInitialDisplay: !isExpoGo(), // This is not supported in Expo Go.
});

Expand Down Expand Up @@ -55,7 +55,7 @@ process.env.EXPO_SKIP_DURING_EXPORT !== 'true' && Sentry.init({
failedRequestTargets: [/.*/],
}),
Sentry.metrics.metricsAggregatorIntegration(),
new Sentry.ReactNativeTracing({
Sentry.reactNativeTracingIntegration({
routingInstrumentation,
}),
);
Expand Down
13 changes: 6 additions & 7 deletions samples/react-native/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ LogBox.ignoreAllLogs();

const isMobileOs = Platform.OS === 'android' || Platform.OS === 'ios';

const reactNavigationInstrumentation =
new Sentry.ReactNavigationInstrumentation({
routeChangeTimeoutMs: 500, // How long it will wait for the route change to complete. Default is 1000ms
enableTimeToInitialDisplay: isMobileOs,
});
const reactNavigationInstrumentation = Sentry.reactNavigationIntegration({
routeChangeTimeoutMs: 500, // How long it will wait for the route change to complete. Default is 1000ms
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved
enableTimeToInitialDisplay: isMobileOs,
});

Sentry.init({
// Replace the example DSN below with your own DSN:
Expand All @@ -64,13 +63,13 @@ Sentry.init({
didCallNativeInit,
);
},
enableUserInteractionTracing: true,
integrations(integrations) {
integrations.push(
new Sentry.ReactNativeTracing({
Sentry.reactNativeTracingIntegration({
// The time to wait in ms until the transaction will be finished, For testing, default is 1000 ms
idleTimeout: 5000,
routingInstrumentation: reactNavigationInstrumentation,
enableUserInteractionTracing: true,
ignoreEmptyBackNavigationTransactions: true,
beforeNavigate: (context: Sentry.ReactNavigationTransactionContext) => {
// Example of not sending a transaction for the screen with the name "Manual Tracker"
Expand Down
3 changes: 2 additions & 1 deletion src/js/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ export class ReactNativeClient extends BaseClient<ReactNativeClientOptions> {
if (routingName) {
this.addIntegration(createIntegration(routingName));
}
const enableUserInteractionTracing = tracing?.options.enableUserInteractionTracing;
const enableUserInteractionTracing =
this._options.enableUserInteractionTracing ?? tracing?.options.enableUserInteractionTracing;
if (enableUserInteractionTracing) {
this.addIntegration(createIntegration('ReactNativeUserInteractionTracing'));
}
Expand Down
4 changes: 3 additions & 1 deletion src/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,13 @@ export { TouchEventBoundary, withTouchEventBoundary } from './touchevents';
export {
ReactNativeTracing,
ReactNavigationV4Instrumentation,
// eslint-disable-next-line deprecation/deprecation
ReactNavigationV5Instrumentation,
ReactNavigationInstrumentation,
ReactNativeNavigationInstrumentation,
RoutingInstrumentation,
reactNativeTracingIntegration,
reactNavigationIntegration,
reactNativeNavigationIntegration,
sentryTraceGesture,
TimeToInitialDisplay,
TimeToFullDisplay,
Expand Down
5 changes: 4 additions & 1 deletion src/js/integrations/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ export function getDefaultIntegrations(options: ReactNativeClientOptions): Integ
if (options.attachViewHierarchy) {
integrations.push(viewHierarchyIntegration());
}
if (options._experiments && typeof options._experiments.profilesSampleRate === 'number') {
if (
options.profilesSampleRate ??
(options._experiments && typeof options._experiments.profilesSampleRate === 'number')
) {
integrations.push(hermesProfilingIntegration());
}
}
Expand Down
41 changes: 39 additions & 2 deletions src/js/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,15 +204,52 @@ export interface BaseReactNativeOptions {
*/
beforeScreenshot?: (event: Event, hint: EventHint) => boolean;

/**
* The sample rate for profiling
* 1.0 will profile all transactions and 0 will profile none.
*/
profilesSampleRate?: number;

/**
* Track the app start time by adding measurements to the first route transaction. If there is no routing instrumentation
* an app start transaction will be started.
*
* Requires performance monitoring to be enabled.
*
* @default true
*/
enableAppStartTracking?: boolean;
krystofwoldrich marked this conversation as resolved.
Show resolved Hide resolved

/**
* Track the slow and frozen frames in the application. Enabling this options will add
* slow and frozen frames measurements to all created root spans (transactions).
*
* @default true
*/
enableNativeFramesTracking?: boolean;

/**
* Track when and how long the JS event loop stalls for. Adds stalls as measurements to all transactions.
*
* @default true
*/
enableStallTracking?: boolean;

/**
* Trace User Interaction events like touch and gestures.
*
* @default false
*/
enableUserInteractionTracing?: boolean;

/**
* Options which are in beta, or otherwise not guaranteed to be stable.
*/
_experiments?: {
[key: string]: unknown;

/**
* The sample rate for profiling
* 1.0 will profile all transactions and 0 will profile none.
* @deprecated Use `profilesSampleRate` in the options root instead.
*/
profilesSampleRate?: number;

Expand Down
12 changes: 8 additions & 4 deletions src/js/profiling/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
import { logger, uuid4 } from '@sentry/utils';
import { Platform } from 'react-native';

import type { ReactNativeClient } from '../client';
import { isHermesEnabled } from '../utils/environment';
import { NATIVE } from '../wrapper';
import { PROFILE_QUEUE } from './cache';
Expand Down Expand Up @@ -109,13 +110,16 @@ export const hermesProfilingIntegration: IntegrationFn = () => {
return false;
}

const client = getClient();
const client = getClient<ReactNativeClient>();
const options = client && client.getOptions();

const profilesSampleRate =
options && options._experiments && typeof options._experiments.profilesSampleRate === 'number'
? options._experiments.profilesSampleRate
: undefined;
options &&
((typeof options.profilesSampleRate === 'number' && options.profilesSampleRate) ||
(options._experiments &&
typeof options._experiments.profilesSampleRate === 'number' &&
options._experiments.profilesSampleRate) ||
undefined);
if (profilesSampleRate === undefined) {
logger.log('[Profiling] Profiling disabled, enable it by setting `profilesSampleRate` option to SDK init call.');
return false;
Expand Down
5 changes: 3 additions & 2 deletions src/js/tracing/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
export { ReactNativeTracing } from './reactnativetracing';
export { ReactNativeTracing, reactNativeTracingIntegration } from './reactnativetracing';

export type { RoutingInstrumentationInstance } from './routingInstrumentation';
export { RoutingInstrumentation } from './routingInstrumentation';

export {
ReactNavigationInstrumentation,
reactNavigationIntegration,
// eslint-disable-next-line deprecation/deprecation
ReactNavigationV5Instrumentation,
} from './reactnavigation';
export { ReactNavigationV4Instrumentation } from './reactnavigationv4';
export { ReactNativeNavigationInstrumentation } from './reactnativenavigation';
export { ReactNativeNavigationInstrumentation, reactNativeNavigationIntegration } from './reactnativenavigation';

export type { ReactNavigationCurrentRoute, ReactNavigationRoute, ReactNavigationTransactionContext } from './types';

Expand Down
11 changes: 11 additions & 0 deletions src/js/tracing/reactnativenavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ export interface NavigationDelegate {
* - `_onComponentWillAppear` is then called AFTER the state change happens due to a dispatch and sets the route context onto the active transaction.
* - If `_onComponentWillAppear` isn't called within `options.routeChangeTimeoutMs` of the dispatch, then the transaction is not sampled and finished.
*/
export const reactNativeNavigationIntegration = (
options: Partial<ReactNativeNavigationOptions> & {
navigation: NavigationDelegate;
},
): ReactNativeNavigationInstrumentation => {
return new ReactNativeNavigationInstrumentation(options.navigation, options);
};

/**
* @deprecated Use `Sentry.reactNativeNavigationIntegration({ navigation })` instead.
*/
export class ReactNativeNavigationInstrumentation extends InternalRoutingInstrumentation {
public static instrumentationName: string = 'react-native-navigation';

Expand Down
51 changes: 31 additions & 20 deletions src/js/tracing/reactnativetracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import type {
} from '@sentry/types';
import { logger } from '@sentry/utils';

import type { ReactNativeClient } from '../client';
import { APP_START_COLD, APP_START_WARM } from '../measurements';
import type { NativeAppStartResponse } from '../NativeRNSentry';
import type { ReactNativeClientOptions } from '../options';
import type { RoutingInstrumentationInstance } from '../tracing/routingInstrumentation';
import { NATIVE } from '../wrapper';
import { NativeFramesInstrumentation } from './nativeframes';
Expand All @@ -30,6 +32,10 @@ import {
setSpanDurationAsMeasurement,
} from './utils';

export const reactNativeTracingIntegration = (options?: Partial<ReactNativeTracingOptions>): ReactNativeTracing => {
return new ReactNativeTracing(options);
};

export interface ReactNativeTracingOptions extends RequestInstrumentationOptions {
/**
* @deprecated Replaced by idleTimeoutMs
Expand Down Expand Up @@ -85,25 +91,22 @@ export interface ReactNativeTracingOptions extends RequestInstrumentationOptions
beforeNavigate: BeforeNavigate;

/**
* Track the app start time by adding measurements to the first route transaction. If there is no routing instrumentation
* an app start transaction will be started.
*
* Default: true
* @deprecated Use `Sentry.init({ enableAppStartTracking })` instead.
*/
enableAppStartTracking: boolean;

/**
* Track slow/frozen frames from the native layer and adds them as measurements to all transactions.
* @deprecated Use `Sentry.init({ enableNativeFramesTracking })` instead.
*/
enableNativeFramesTracking: boolean;

/**
* Track when and how long the JS event loop stalls for. Adds stalls as measurements to all transactions.
* @deprecated Use `Sentry.init({ enableStallTracking })` instead.
*/
enableStallTracking: boolean;

/**
* Trace User Interaction events like touch and gestures.
* @deprecated Use `Sentry.init({ enableUserInteractionTracing })` instead.
*/
enableUserInteractionTracing: boolean;
}
Expand All @@ -125,7 +128,7 @@ const defaultReactNativeTracingOptions: ReactNativeTracingOptions = {
};

/**
* Tracing integration for React Native.
* @deprecated Use `Sentry.reactNativeTracingIntegration()` instead.
*/
export class ReactNativeTracing implements Integration {
/**
Expand Down Expand Up @@ -197,7 +200,7 @@ export class ReactNativeTracing implements Integration {
getCurrentHub: () => Hub,
): Promise<void> {
const hub = getCurrentHub();
const client = hub.getClient();
const client = hub.getClient<ReactNativeClient>();
const clientOptions = client && client.getOptions();

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down Expand Up @@ -241,15 +244,15 @@ export class ReactNativeTracing implements Integration {
);
}

if (enableAppStartTracking) {
this._instrumentAppStart().then(undefined, (reason: unknown) => {
if (clientOptions?.enableAppStartTracking ?? enableAppStartTracking) {
this._instrumentAppStart(clientOptions).then(undefined, (reason: unknown) => {
logger.error(`[ReactNativeTracing] Error while instrumenting app start:`, reason);
});
}

this._enableNativeFramesTracking(addGlobalEventProcessor);
this._enableNativeFramesTracking(addGlobalEventProcessor, clientOptions);

if (enableStallTracking) {
if (clientOptions?.enableStallTracking ?? enableStallTracking) {
this.stallTrackingInstrumentation = new StallTrackingInstrumentation();
}

Expand Down Expand Up @@ -321,7 +324,10 @@ export class ReactNativeTracing implements Integration {
op: string;
}): TransactionType | undefined {
const { elementId, op } = userInteractionId;
if (!this.options.enableUserInteractionTracing) {

const clientOptions = this._getCurrentHub?.()?.getClient<ReactNativeClient>()?.getOptions();

if (!(clientOptions?.enableUserInteractionTracing ?? this.options.enableUserInteractionTracing)) {
logger.log('[ReactNativeTracing] User Interaction Tracing is disabled.');
return;
}
Expand Down Expand Up @@ -376,22 +382,27 @@ export class ReactNativeTracing implements Integration {
/**
* Enables or disables native frames tracking based on the `enableNativeFramesTracking` option.
*/
private _enableNativeFramesTracking(addGlobalEventProcessor: (callback: EventProcessor) => void): void {
if (this.options.enableNativeFramesTracking && !NATIVE.enableNative) {
private _enableNativeFramesTracking(
addGlobalEventProcessor: (callback: EventProcessor) => void,
clientOptions: ReactNativeClientOptions | undefined,
): void {
const enableNativeFramesTracking =
clientOptions?.enableNativeFramesTracking ?? this.options.enableNativeFramesTracking;
if (enableNativeFramesTracking && !NATIVE.enableNative) {
// Do not enable native frames tracking if native is not available.
logger.warn(
'[ReactNativeTracing] NativeFramesTracking is not available on the Web, Expo Go and other platforms without native modules.',
);
return;
}

if (!this.options.enableNativeFramesTracking && NATIVE.enableNative) {
if (!enableNativeFramesTracking && NATIVE.enableNative) {
// Disable native frames tracking when native available and option is false.
NATIVE.disableNativeFramesTracking();
return;
}

if (!this.options.enableNativeFramesTracking) {
if (!enableNativeFramesTracking) {
return;
}

Expand Down Expand Up @@ -433,8 +444,8 @@ export class ReactNativeTracing implements Integration {
* Instruments the app start measurements on the first route transaction.
* Starts a route transaction if there isn't routing instrumentation.
*/
private async _instrumentAppStart(): Promise<void> {
if (!this.options.enableAppStartTracking || !NATIVE.enableNative) {
private async _instrumentAppStart(clientOptions: ReactNativeClientOptions | undefined): Promise<void> {
if (!(clientOptions?.enableAppStartTracking ?? this.options.enableAppStartTracking) || !NATIVE.enableNative) {
return;
}

Expand Down
9 changes: 9 additions & 0 deletions src/js/tracing/reactnavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ const defaultOptions: ReactNavigationOptions = {
* - `_onStateChange` is then called AFTER the state change happens due to a dispatch and sets the route context onto the active transaction.
* - If `_onStateChange` isn't called within `STATE_CHANGE_TIMEOUT_DURATION` of the dispatch, then the transaction is not sampled and finished.
*/
export const reactNavigationIntegration = (
options: Partial<ReactNavigationOptions> = {},
): ReactNavigationInstrumentation => {
return new ReactNavigationInstrumentation(options);
};

/**
* @deprecated Please use `Sentry.reactNavigationIntegration()`
*/
export class ReactNavigationInstrumentation extends InternalRoutingInstrumentation {
public static instrumentationName: string = 'react-navigation-v5';

Expand Down
2 changes: 2 additions & 0 deletions src/js/tracing/reactnavigationv4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const defaultOptions: ReactNavigationV4Options = {
/**
* Instrumentation for React-Navigation V4.
* Register the app container with `registerAppContainer` to use, or see docs for more details.
*
* @deprecated Support for `react-navigation@4` and older will be removed in the next major version of the SDK. Please upgrade to `react-navigation@5` or newer.
*/
class ReactNavigationV4Instrumentation extends InternalRoutingInstrumentation {
public static instrumentationName: string = 'react-navigation-v4';
Expand Down
Loading
Loading