From e5f8a99bff6cbc753a24a49c9e776b92f346c9a2 Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Sun, 13 Jul 2025 06:58:20 +0200 Subject: [PATCH] refactor: extract sentry --- apps/client/src/AppRouter.tsx | 39 ++++---------------- apps/client/src/sentry.config.ts | 62 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 33 deletions(-) diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index ef5777b4d..0b9b2cf8d 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -1,22 +1,12 @@ import React from 'react'; -import { - createRoutesFromChildren, - matchRoutes, - Navigate, - Route, - Routes, - useLocation, - useNavigationType, -} from 'react-router-dom'; -import * as Sentry from '@sentry/react'; +import { Navigate, Route } from 'react-router-dom'; import { useClientPath } from './common/hooks/useClientPath'; import Log from './features/log/Log'; import withPreset from './features/PresetWrapper'; import withData from './features/viewers/ViewWrapper'; import ViewLoader from './views/ViewLoader'; -import { ONTIME_VERSION } from './ONTIME_VERSION'; -import { sentryDsn, sentryRecommendedIgnore } from './sentry.config'; +import { initializeSentry } from './sentry.config'; const Editor = React.lazy(() => import('./views/editor/ProtectedEditor')); const Cuesheet = React.lazy(() => import('./views/cuesheet/ProtectedCuesheet')); @@ -44,25 +34,8 @@ const RundownPanel = React.lazy(() => import('./features/rundown/RundownExport') const TimerControl = React.lazy(() => import('./features/control/playback/TimerControlExport')); const MessageControl = React.lazy(() => import('./features/control/message/MessageControlExport')); -Sentry.init({ - dsn: sentryDsn, - integrations: [ - Sentry.reactRouterV6BrowserTracingIntegration({ - useEffect: React.useEffect, - useLocation, - useNavigationType, - createRoutesFromChildren, - matchRoutes, - }), - ], - tracesSampleRate: 0.3, - release: ONTIME_VERSION, - enabled: import.meta.env.PROD, - ignoreErrors: [...sentryRecommendedIgnore, /Unable to preload CSS/i, /dynamically imported module/i], - denyUrls: [/extensions\//i, /^chrome:\/\//i, /^chrome-extension:\/\//i], -}); - -const SentryRoutes = Sentry.withSentryReactRouterV6Routing(Routes); +// Initialize Sentry with our configuration +const SentryRouter = initializeSentry(); export default function AppRouter() { // handle client path changes @@ -70,7 +43,7 @@ export default function AppRouter() { return ( - + } /> {/*/!* Send to default if nothing found *!/*/} } /> - + ); } diff --git a/apps/client/src/sentry.config.ts b/apps/client/src/sentry.config.ts index f7af7d1ef..8db7b3560 100644 --- a/apps/client/src/sentry.config.ts +++ b/apps/client/src/sentry.config.ts @@ -1,3 +1,10 @@ +import React from 'react'; +import { createRoutesFromChildren, matchRoutes, useLocation, useNavigationType } from 'react-router-dom'; +import { Routes } from 'react-router-dom'; +import * as Sentry from '@sentry/react'; + +import { ONTIME_VERSION } from './ONTIME_VERSION'; + // https://docs.sentry.io/platforms/javascript/configuration/filtering/#decluttering-sentry export const sentryRecommendedIgnore = [ // Random plugins/extensions @@ -23,3 +30,58 @@ export const sentryRecommendedIgnore = [ 'conduitPage', ]; export const sentryDsn = 'https://5e4d2c4b57ab409cb98d4c08b2014755@o4504288369836032.ingest.sentry.io/4504288371343360'; + +export const initializeSentry = () => { + Sentry.init({ + dsn: sentryDsn, + integrations: [ + Sentry.reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ], + tracesSampleRate: 0.3, + release: ONTIME_VERSION, + enabled: import.meta.env.PROD, + ignoreErrors: [ + ...sentryRecommendedIgnore, + // Dynamic imports and chunks + /Unable to preload CSS/i, + /dynamically imported module/i, + /Failed to fetch dynamically imported module/i, + /Loading chunk \d+ failed/i, + /Loading CSS chunk \d+ failed/i, + /ChunkLoadError/i, + /Loading module .+ failed/i, + // Data/offline related errors + /Cannot read propert.* of undefined/i, + /Cannot read propert.* of null/i, + /Network request failed/i, + /Failed to fetch/i, + /NetworkError/i, + /The operation couldn't be completed/i, + ], + denyUrls: [/extensions\//i, /^chrome:\/\//i, /^chrome-extension:\/\//i], + beforeSend(event) { + // Drop errors that happen during known data-unavailable states + const error = event.exception?.values?.[0]?.value; + if ( + error && + (error.includes('Cannot read property') || + error.includes('Cannot read properties') || + error.includes('Network request failed') || + error.includes('Failed to fetch')) + ) { + // Don't send these errors to Sentry since they're expected when data isn't available + return null; + } + + return event; + }, + }); + + return Sentry.withSentryReactRouterV6Routing(Routes); +};