Skip to content

Commit

Permalink
misc(deprecations): Add Tracing deprecations and replacements (#4073)
Browse files Browse the repository at this point in the history
  • Loading branch information
krystofwoldrich authored Sep 11, 2024
1 parent 70b4797 commit d43a46b
Show file tree
Hide file tree
Showing 22 changed files with 216 additions and 56 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
module.exports = withSentryConfig(getDefaultConfig(__dirname), { includeWebReplay: true });
```

### Changes

- React Native Tracing Deprecations ([#4073](https://github.com/getsentry/sentry-react-native/pull/4073))
- `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
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;

/**
* 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

0 comments on commit d43a46b

Please sign in to comment.