diff --git a/static/app/views/app/index.tsx b/static/app/views/app/index.tsx index e0efe2a18e1bda..f616a08b965de1 100644 --- a/static/app/views/app/index.tsx +++ b/static/app/views/app/index.tsx @@ -32,6 +32,7 @@ import {useLocation} from 'sentry/utils/useLocation'; import {useUser} from 'sentry/utils/useUser'; import type {InstallWizardProps} from 'sentry/views/admin/installWizard'; import {AsyncSDKIntegrationContextProvider} from 'sentry/views/app/asyncSDKIntegrationProvider'; +import LastKnownRouteContextProvider from 'sentry/views/lastKnownRouteContextProvider'; import {OrganizationContextProvider} from 'sentry/views/organizationContext'; import RouteAnalyticsContextProvider from 'sentry/views/routeAnalyticsContextProvider'; @@ -241,22 +242,24 @@ function App({children, params}: Props) { return ( - - - - - - - - - - {renderBody()} - - - - - - + + + + + + + + + + + {renderBody()} + + + + + + + ); } diff --git a/static/app/views/lastKnownRouteContextProvider.tsx b/static/app/views/lastKnownRouteContextProvider.tsx new file mode 100644 index 00000000000000..38fae90648a579 --- /dev/null +++ b/static/app/views/lastKnownRouteContextProvider.tsx @@ -0,0 +1,31 @@ +import {createContext, useContext} from 'react'; + +import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes'; +import usePrevious from 'sentry/utils/usePrevious'; +import {useRoutes} from 'sentry/utils/useRoutes'; + +interface Props { + children: React.ReactNode; +} + +export const LastKnownRouteContext = createContext(''); + +export function useLastKnownRoute() { + return useContext(LastKnownRouteContext); +} + +/** + * This provider tracks the last known route that the user has navigated to. + * This is used to better group issues when we hit "route not found" errors. + */ +export default function LastKnownRouteContextProvider({children}: Props) { + const route = useRoutes(); + const prevRoute = usePrevious(route); + const lastKnownRoute = getRouteStringFromRoutes(prevRoute); + + return ( + + {children} + + ); +} diff --git a/static/app/views/routeNotFound.tsx b/static/app/views/routeNotFound.tsx index e02dde44fbc034..c92f7ca4aecc62 100644 --- a/static/app/views/routeNotFound.tsx +++ b/static/app/views/routeNotFound.tsx @@ -8,11 +8,13 @@ import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import Sidebar from 'sentry/components/sidebar'; import {t} from 'sentry/locale'; import type {RouteComponentProps} from 'sentry/types/legacyReactRouter'; +import {useLastKnownRoute} from 'sentry/views/lastKnownRouteContextProvider'; type Props = RouteComponentProps<{}, {}>; function RouteNotFound({router, location}: Props) { const {pathname, search, hash} = location; + const lastKnownRoute = useLastKnownRoute(); const isMissingSlash = pathname[pathname.length - 1] !== '/'; @@ -27,13 +29,14 @@ function RouteNotFound({router, location}: Props) { scope.setFingerprint(['RouteNotFound']); scope.setTag('isMissingSlash', isMissingSlash); scope.setTag('pathname', pathname); + scope.setTag('lastKnownRoute', lastKnownRoute); scope.setTag( 'reactRouterVersion', window.__SENTRY_USING_REACT_ROUTER_SIX ? '6' : '3' ); Sentry.captureException(new Error('Route not found')); }); - }, [pathname, search, hash, isMissingSlash, router]); + }, [pathname, search, hash, isMissingSlash, router, lastKnownRoute]); if (isMissingSlash) { return null;