Skip to content
Closed
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
12 changes: 6 additions & 6 deletions src/js/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Alert } from 'react-native';
import { createIntegration } from './integrations/factory';
import { defaultSdkInfo } from './integrations/sdkinfo';
import type { ReactNativeClientOptions } from './options';
import { ReactNativeTracing } from './tracing';
import type { ReactNativeTracing } from './tracing';
import { createUserFeedbackEnvelope, items } from './utils/envelope';
import { ignoreRequireCycleLogs } from './utils/ignorerequirecyclelogs';
import { mergeOutcomes } from './utils/outcome';
Expand Down Expand Up @@ -90,12 +90,12 @@ export class ReactNativeClient extends BaseClient<ReactNativeClientOptions> {
}

/**
* Sets up the integrations
* @inheritdoc
*/
public setupIntegrations(): void {
super.setupIntegrations();
const tracing = this.getIntegration(ReactNativeTracing);
const routingName = tracing?.options.routingInstrumentation?.name;
protected _setupIntegrations(): void {
super._setupIntegrations();
const tracing = this.getIntegrationByName('ReactNativeTracing') as ReactNativeTracing;
const routingName = tracing?.options?.routingInstrumentation?.name;
if (routingName) {
this.addIntegration(createIntegration(routingName));
}
Expand Down
4 changes: 2 additions & 2 deletions src/js/profiling/integration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable complexity */
import type { Hub } from '@sentry/core';
import { getActiveTransaction } from '@sentry/core';
import { getActiveTransaction, spanIsSampled } from '@sentry/core';
import type { Envelope, Event, EventProcessor, Integration, ThreadCpuProfile, Transaction } from '@sentry/types';
import { logger, uuid4 } from '@sentry/utils';
import { Platform } from 'react-native';
Expand Down Expand Up @@ -112,7 +112,7 @@ export class HermesProfiling implements Integration {
};

private _shouldStartProfiling = (transaction: Transaction): boolean => {
if (!transaction.sampled) {
if (!spanIsSampled(transaction)) {
logger.log('[Profiling] Transaction is not sampled, skipping profiling');
return false;
}
Expand Down
23 changes: 12 additions & 11 deletions src/js/sdk.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable complexity */
import type { Scope } from '@sentry/core';
import { getIntegrationsToSetup, Hub, initAndBind, makeMain, setExtra } from '@sentry/core';
import { getClient, getIntegrationsToSetup, Hub, initAndBind, makeMain, setExtra, withScope as coreWithScope } from '@sentry/core';
import {
defaultStackParser,
getCurrentHub,
Expand All @@ -16,7 +16,8 @@ import type { ReactNativeClientOptions, ReactNativeOptions, ReactNativeWrapperOp
import { shouldEnableNativeNagger } from './options';
import { ReactNativeScope } from './scope';
import { TouchEventBoundary } from './touchevents';
import { ReactNativeProfiler, ReactNativeTracing } from './tracing';
import type { ReactNativeTracing } from './tracing';
import { ReactNativeProfiler } from './tracing';
import { DEFAULT_BUFFER_SIZE, makeNativeTransportFactory } from './transports/native';
import { makeUtf8TextEncoder } from './transports/TextEncoder';
import { getDefaultEnvironment, isExpoGo } from './utils/environment';
Expand Down Expand Up @@ -108,7 +109,7 @@ export function wrap<P extends Record<string, unknown>>(
RootComponent: React.ComponentType<P>,
options?: ReactNativeWrapperOptions
): React.ComponentType<P> {
const tracingIntegration = getCurrentHub().getIntegration(ReactNativeTracing);
const tracingIntegration = getClient()?.getIntegrationByName?.('ReactNativeTracing') as ReactNativeTracing | undefined;
if (tracingIntegration) {
tracingIntegration.useAppStartWithProfiler = true;
}
Expand Down Expand Up @@ -154,10 +155,7 @@ export function setDist(dist: string): void {
* Use this only for testing purposes.
*/
export function nativeCrash(): void {
const client = getCurrentHub().getClient<ReactNativeClient>();
if (client) {
client.nativeCrash();
}
NATIVE.nativeCrash();
}

/**
Expand All @@ -166,7 +164,7 @@ export function nativeCrash(): void {
*/
export async function flush(): Promise<boolean> {
try {
const client = getCurrentHub().getClient<ReactNativeClient>();
const client = getClient();

if (client) {
const result = await client.flush();
Expand All @@ -186,7 +184,7 @@ export async function flush(): Promise<boolean> {
*/
export async function close(): Promise<void> {
try {
const client = getCurrentHub().getClient<ReactNativeClient>();
const client = getClient();

if (client) {
await client.close();
Expand All @@ -200,7 +198,7 @@ export async function close(): Promise<void> {
* Captures user feedback and sends it to Sentry.
*/
export function captureUserFeedback(feedback: UserFeedback): void {
getCurrentHub().getClient<ReactNativeClient>()?.captureUserFeedback(feedback);
getClient<ReactNativeClient>()?.captureUserFeedback(feedback);
}

/**
Expand All @@ -225,12 +223,14 @@ export function withScope<T>(callback: (scope: Scope) => T): T | undefined {
return undefined;
}
};
return getCurrentHub().withScope(safeCallback);
return coreWithScope(safeCallback);
}

/**
* Callback to set context information onto the scope.
* @param callback Callback function that receives Scope.
*
* @deprecated Use `getScope()` directly.
*/
export function configureScope(callback: (scope: Scope) => void): ReturnType<Hub['configureScope']> {
const safeCallback = (scope: Scope): void => {
Expand All @@ -240,5 +240,6 @@ export function configureScope(callback: (scope: Scope) => void): ReturnType<Hub
logger.error('Error while running configureScope callback', e);
}
};
// eslint-disable-next-line deprecation/deprecation
getCurrentHub().configureScope(safeCallback);
}
8 changes: 4 additions & 4 deletions src/js/touchevents.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { addBreadcrumb, getCurrentHub } from '@sentry/core';
import { addBreadcrumb, getClient } from '@sentry/core';
import type { SeverityLevel } from '@sentry/types';
import { logger } from '@sentry/utils';
import * as React from 'react';
import type { GestureResponderEvent} from 'react-native';
import { StyleSheet, View } from 'react-native';

import { createIntegration } from './integrations/factory';
import { ReactNativeTracing } from './tracing';
import type { ReactNativeTracing } from './tracing';
import { UI_ACTION_TOUCH } from './tracing/ops';

export type TouchEventBoundaryProps = {
Expand Down Expand Up @@ -88,10 +88,10 @@ class TouchEventBoundary extends React.Component<TouchEventBoundaryProps> {
* Registers the TouchEventBoundary as a Sentry Integration.
*/
public componentDidMount(): void {
const client = getCurrentHub().getClient();
const client = getClient();
client?.addIntegration?.(createIntegration(this.name));
if (!this._tracingIntegration && client) {
this._tracingIntegration = client.getIntegration(ReactNativeTracing);
this._tracingIntegration = client.getIntegrationByName?.('ReactNativeTracing') as ReactNativeTracing|| null;
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/js/tracing/addTracingExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,14 @@ const _patchStartTransaction = (originalStartTransaction: StartTransactionFuncti
if (reactNativeTracing) {
reactNativeTracing.onTransactionStart(transaction);

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

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

return originalFinish.apply(transaction, [endTimestamp]);
return originalFinish(endTimestamp);
};
}

Expand Down
22 changes: 16 additions & 6 deletions src/js/tracing/nativeframes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Span, Transaction } from '@sentry/core';
import { type Span, type Transaction,spanToJSON } from '@sentry/core';
import type { Event, EventProcessor, Measurements, MeasurementUnit } from '@sentry/types';
import { logger, timestampInSeconds } from '@sentry/utils';

Expand Down Expand Up @@ -169,6 +169,11 @@ export class NativeFramesInstrumentation {
* Fetch finish frames for a transaction at the current time. Calls any awaiting listeners.
*/
private async _fetchFramesForTransaction(transaction: Transaction): Promise<void> {
const traceId = spanToJSON(transaction).trace_id;
if (!traceId) {
return;
}

const startFrames = transaction.data.__startFrames as NativeFramesResponse | undefined;

// This timestamp marks when the finish frames were retrieved. It should be pretty close to the transaction finish.
Expand All @@ -178,12 +183,12 @@ export class NativeFramesInstrumentation {
finishFrames = await NATIVE.fetchNativeFrames();
}

this._finishFrames.set(transaction.traceId, {
this._finishFrames.set(traceId, {
nativeFrames: finishFrames,
timestamp,
});

this._framesListeners.get(transaction.traceId)?.();
this._framesListeners.get(traceId)?.();

setTimeout(() => this._cancelFinishFrames(transaction), 2000);
}
Expand All @@ -192,11 +197,16 @@ export class NativeFramesInstrumentation {
* On a finish frames failure, we cancel the await.
*/
private _cancelFinishFrames(transaction: Transaction): void {
if (this._finishFrames.has(transaction.traceId)) {
this._finishFrames.delete(transaction.traceId);
const traceId = spanToJSON(transaction).trace_id;
if (!traceId) {
return;
}

if (this._finishFrames.has(traceId)) {
this._finishFrames.delete(traceId);

logger.log(
`[NativeFrames] Native frames timed out for ${transaction.op} transaction ${transaction.name}. Not adding native frames measurements.`,
`[NativeFrames] Native frames timed out for ${spanToJSON(transaction).op} transaction ${spanToJSON(transaction).description}. Not adding native frames measurements.`,
);
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/js/tracing/reactnativeprofiler.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getCurrentHub, Profiler } from '@sentry/react';
import { spanToJSON } from '@sentry/core';
import { getClient, Profiler } from '@sentry/react';

import { createIntegration } from '../integrations/factory';
import { ReactNativeTracing } from './reactnativetracing';
import type { ReactNativeTracing } from './reactnativetracing';

const ReactNativeProfilerGlobalState = {
appStartReported: false,
Expand All @@ -28,8 +29,7 @@ export class ReactNativeProfiler extends Profiler {
* Notifies the Tracing integration that the app start has finished.
*/
private _reportAppStart(): void {
const hub = getCurrentHub();
const client = hub.getClient();
const client = getClient();

if (!client) {
// We can't use logger here because this will be logged before the `Sentry.init`.
Expand All @@ -40,11 +40,11 @@ export class ReactNativeProfiler extends Profiler {

client.addIntegration && client.addIntegration(createIntegration(this.name));

const tracingIntegration = hub.getIntegration(ReactNativeTracing);
const endTimestamp = this._mountSpan && typeof spanToJSON(this._mountSpan).timestamp
const tracingIntegration = client.getIntegrationByName && client.getIntegrationByName<ReactNativeTracing>('ReactNativeTracing');
tracingIntegration
&& this._mountSpan
&& typeof this._mountSpan.endTimestamp !== 'undefined'
&& typeof endTimestamp === 'number'
// The first root component mount is the app start finish.
&& tracingIntegration.onAppStartFinish(this._mountSpan.endTimestamp);
&& tracingIntegration.onAppStartFinish(endTimestamp);
}
}
14 changes: 7 additions & 7 deletions src/js/tracing/reactnativetracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { RequestInstrumentationOptions } from '@sentry/browser';
import { defaultRequestInstrumentationOptions, instrumentOutgoingRequests } from '@sentry/browser';
import type { Hub, IdleTransaction, Transaction } from '@sentry/core';
import { getActiveTransaction, getCurrentHub, startIdleTransaction } from '@sentry/core';
import { getActiveTransaction, getCurrentHub, spanToJSON, startIdleTransaction } from '@sentry/core';
import type {
Event,
EventProcessor,
Expand Down Expand Up @@ -272,7 +272,7 @@ export class ReactNativeTracing implements Integration {
* To be called on a transaction start. Can have async methods
*/
public onTransactionStart(transaction: Transaction): void {
if (isNearToNow(transaction.startTimestamp)) {
if (isNearToNow(spanToJSON(transaction).start_timestamp)) {
// Only if this method is called at or within margin of error to the start timestamp.
this.nativeFramesInstrumentation?.onTransactionStart(transaction);
this.stallTrackingInstrumentation?.onTransactionStart(transaction);
Expand Down Expand Up @@ -324,11 +324,11 @@ export class ReactNativeTracing implements Integration {

const hub = this._getCurrentHub?.() || getCurrentHub();
const activeTransaction = getActiveTransaction(hub);
const activeTransactionIsNotInteraction =
activeTransaction?.spanId !== this._inflightInteractionTransaction?.spanId;
const activeTransactionIsNotInteraction = !activeTransaction || !this._inflightInteractionTransaction ||
spanToJSON(activeTransaction).span_id !== spanToJSON(this._inflightInteractionTransaction).span_id;
if (activeTransaction && activeTransactionIsNotInteraction) {
logger.warn(
`[ReactNativeTracing] Did not create ${op} transaction because active transaction ${activeTransaction.name} exists on the scope.`,
`[ReactNativeTracing] Did not create ${op} transaction because active transaction ${spanToJSON(activeTransaction).description} exists on the scope.`,
);
return;
}
Expand Down Expand Up @@ -534,10 +534,10 @@ export class ReactNativeTracing implements Integration {

if (this._inflightInteractionTransaction) {
logger.log(
`[ReactNativeTracing] Canceling ${this._inflightInteractionTransaction.op} transaction because navigation ${context.op}.`,
`[ReactNativeTracing] Canceling ${spanToJSON(this._inflightInteractionTransaction).op} transaction because navigation ${context.op}.`,
);
this._inflightInteractionTransaction.setStatus('cancelled');
this._inflightInteractionTransaction.finish();
this._inflightInteractionTransaction.end();
}

const { finalTimeoutMs } = this.options;
Expand Down
13 changes: 7 additions & 6 deletions src/js/tracing/stalltracking.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable max-lines */
import type { IdleTransaction, Span, Transaction } from '@sentry/core';
import { type IdleTransaction, type Span, type Transaction,spanToJSON } from '@sentry/core';
import type { Measurements, MeasurementUnit } from '@sentry/types';
import { logger, timestampInSeconds } from '@sentry/utils';
import type { AppStateStatus } from 'react-native';
Expand Down Expand Up @@ -118,8 +118,9 @@ export class StallTrackingInstrumentation {
originalSpanFinish.apply(span, [endTimestamp]);

// The span should set a timestamp, so this would be defined.
if (span.endTimestamp) {
this._markSpanFinish(transaction, span.endTimestamp);
const finalEndTimestamp = spanToJSON(span).timestamp;
if (finalEndTimestamp) {
this._markSpanFinish(transaction, finalEndTimestamp);
}
};
};
Expand All @@ -144,10 +145,10 @@ export class StallTrackingInstrumentation {
return;
}

const endTimestamp = passedEndTimestamp ?? transaction.endTimestamp;
const endTimestamp = passedEndTimestamp ?? spanToJSON(transaction).timestamp;

const spans = transaction.spanRecorder ? transaction.spanRecorder.spans : [];
const finishedSpanCount = spans.reduce((count, s) => (s !== transaction && s.endTimestamp ? count + 1 : count), 0);
const finishedSpanCount = spans.reduce((count, s) => (s !== transaction && spanToJSON(s).timestamp ? count + 1 : count), 0);

const trimEnd = transaction.toContext().trimEnd;
const endWillBeTrimmed = trimEnd && finishedSpanCount > 0;
Expand All @@ -170,7 +171,7 @@ export class StallTrackingInstrumentation {

// There will be cancelled spans, which means that the end won't be trimmed
const spansWillBeCancelled = spans.some(
s => s !== transaction && s.startTimestamp < endTimestamp && !s.endTimestamp,
s => s !== transaction && s.startTimestamp < endTimestamp && !spanToJSON(s).timestamp,
);

if (endWillBeTrimmed && !spansWillBeCancelled) {
Expand Down
12 changes: 6 additions & 6 deletions src/js/tracing/transaction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type BeforeFinishCallback, type IdleTransaction } from '@sentry/core';
import { type BeforeFinishCallback, type IdleTransaction,spanToJSON } from '@sentry/core';
import { logger } from '@sentry/utils';
import type { AppStateStatus } from 'react-native';
import { AppState } from 'react-native';
Expand All @@ -10,10 +10,10 @@ import { AppState } from 'react-native';
export const onlySampleIfChildSpans: BeforeFinishCallback = (transaction: IdleTransaction): void => {
const spansCount =
transaction.spanRecorder &&
transaction.spanRecorder.spans.filter(span => span.spanId !== transaction.spanId).length;
transaction.spanRecorder.spans.filter(span => spanToJSON(span).span_id !== spanToJSON(transaction).span_id).length;

if (!spansCount || spansCount <= 0) {
logger.log(`Not sampling as ${transaction.op} transaction has no child spans.`);
logger.log(`Not sampling as ${spanToJSON(transaction).op} transaction has no child spans.`);
transaction.sampled = false;
}
};
Expand All @@ -24,13 +24,13 @@ export const onlySampleIfChildSpans: BeforeFinishCallback = (transaction: IdleTr
export const cancelInBackground = (transaction: IdleTransaction): void => {
const subscription = AppState.addEventListener('change', (newState: AppStateStatus) => {
if (newState === 'background') {
logger.debug(`Setting ${transaction.op} transaction to cancelled because the app is in the background.`);
logger.debug(`Setting ${spanToJSON(transaction).op} transaction to cancelled because the app is in the background.`);
transaction.setStatus('cancelled');
transaction.finish();
transaction.end();
}
});
transaction.registerBeforeFinishCallback(() => {
logger.debug(`Removing AppState listener for ${transaction.op} transaction.`);
logger.debug(`Removing AppState listener for ${spanToJSON(transaction).op} transaction.`);
subscription.remove();
});
};
Loading