Skip to content
Draft
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
48 changes: 42 additions & 6 deletions packages/core/src/js/tracing/reactnavigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,30 @@ export const INTEGRATION_NAME = 'ReactNavigation';

const NAVIGATION_HISTORY_MAX_SIZE = 200;

/**
* Builds a full path from the navigation state by traversing nested navigators.
* For example, with nested navigators: "Home/Settings/Profile"
*/
function getPathFromState(state?: NavigationState): string | undefined {
if (!state) {
return undefined;
}

const routeNames: string[] = [];
let currentState: NavigationState | undefined = state;

while (currentState) {
const index: number = currentState.index ?? 0;
const route: NavigationRoute | undefined = currentState.routes[index];
if (route?.name) {
routeNames.push(route.name);
}
currentState = route?.state;
}

return routeNames.length > 0 ? routeNames.join('/') : undefined;
}

interface ReactNavigationIntegrationOptions {
/**
* How long the instrumentation will wait for the route to mount after a change has been initiated,
Expand Down Expand Up @@ -319,16 +343,21 @@ export const reactNavigationIntegration = ({

const routeHasBeenSeen = recentRouteKeys.includes(route.key);

navigationProcessingSpan?.updateName(`Navigation dispatch to screen ${route.name} mounted`);
// Get the full navigation path for nested navigators
const navigationState = navigationContainer.getState();
const fullRoutePath = getPathFromState(navigationState);
const routeName = fullRoutePath || route.name;

navigationProcessingSpan?.updateName(`Navigation dispatch to screen ${routeName} mounted`);
navigationProcessingSpan?.setStatus({ code: SPAN_STATUS_OK });
navigationProcessingSpan?.end(stateChangedTimestamp);
navigationProcessingSpan = undefined;

if (spanToJSON(latestNavigationSpan).description === DEFAULT_NAVIGATION_SPAN_NAME) {
latestNavigationSpan.updateName(route.name);
latestNavigationSpan.updateName(routeName);
}
latestNavigationSpan.setAttributes({
'route.name': route.name,
'route.name': routeName,
'route.key': route.key,
// TODO: filter PII params instead of dropping them all
// 'route.params': {},
Expand All @@ -347,14 +376,14 @@ export const reactNavigationIntegration = ({
addBreadcrumb({
category: 'navigation',
type: 'navigation',
message: `Navigation to ${route.name}`,
message: `Navigation to ${routeName}`,
data: {
from: previousRoute?.name,
to: route.name,
to: routeName,
},
});

tracing?.setCurrentRoute(route.name);
tracing?.setCurrentRoute(routeName);

pushRecentRouteKey(route.key);
latestRoute = route;
Expand Down Expand Up @@ -412,11 +441,18 @@ export interface NavigationRoute {
key: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params?: Record<string, any>;
state?: NavigationState;
}

interface NavigationState {
index?: number;
routes: NavigationRoute[];
}

interface NavigationContainer {
addListener: (type: string, listener: (event?: unknown) => void) => void;
getCurrentRoute: () => NavigationRoute;
getState: () => NavigationState | undefined;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion samples/expo/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const navigationIntegration = Sentry.reactNavigationIntegration({

Sentry.init({
// Replace the example DSN below with your own DSN:
dsn: SENTRY_INTERNAL_DSN,
dsn: 'https://c96d5b6136315b7a6cef6230a38b4842@o4509786567475200.ingest.de.sentry.io/4510182962298960',
debug: true,
environment: 'dev',
enableLogs: true,
Expand Down
Loading