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:
[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;