From 07396b7608ee6a7857a35641626b70027f6bc95d Mon Sep 17 00:00:00 2001 From: Michelle Zhang <56095982+michellewzhang@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:56:49 -0700 Subject: [PATCH] fix(routes): track last route in routeNotFound (#77835) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### The problem In Sentry today, tons of “route not found” events are grouped together into one issue, even if the suspect routes/transactions are not related. This poses a problem because the relevant teams are not alerted to these issues, which means 1) we’re not able to triage & fix these issues as quickly, and 2) we do not have proper scope of the problem areas. See an issue example [here](https://sentry.sentry.io/issues/496412334/events/c7dce88ad52944a095d672d0d114d7da/?project=11276&referrer=replay-errors), and notice that not all the [events](https://sentry.sentry.io/issues/496412334/events/c720b6d1807849b9ae0859f9b165a344/events/?project=11276&referrer=next-event) have the same transaction. ### The goal Improve our grouping for “route not found” issues so that the events are split up into better issues — specifically, based on where the last known route was. That way, we can have more relevant issues grouped by (hopefully) the suspect problem route & triage more effectively. ### Next steps Next step would be setting the fingerprinting rules in the `javascript` project to include the new tag-based grouping rule. This is in project settings: SCR-20240919-tsyc [notion doc](https://www.notion.so/sentry/Improve-Route-not-found-issue-grouping-1068b10e4b5d80129e5dc9b84b1b5eb5?showMoveTo=true&saveParent=true) --- static/app/views/app/index.tsx | 35 ++++++++++--------- .../views/lastKnownRouteContextProvider.tsx | 31 ++++++++++++++++ static/app/views/routeNotFound.tsx | 5 ++- 3 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 static/app/views/lastKnownRouteContextProvider.tsx 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;