diff --git a/env.ts b/env.ts index 1d6356b4e2..562ac024d2 100644 --- a/env.ts +++ b/env.ts @@ -6,10 +6,13 @@ export default defineConfig({ APP_ENVIRONMENT: Schema.enum(['development', 'testing', 'staging', 'production'] as const), APP_MAPBOX_ACCESS_TOKEN: Schema.string(), APP_RISK_API_ENDPOINT: Schema.string({ format: 'url', protocol: true }), - APP_SENTRY_DSN: Schema.string.optional(), - APP_SENTRY_NORMALIZE_DEPTH: Schema.number.optional(), - APP_SENTRY_TRACES_SAMPLE_RATE: Schema.number.optional(), + APP_RISK_ADMIN_URL: Schema.string({ format: 'url', protocol: true }), APP_SHOW_ENV_BANNER: Schema.boolean.optional(), APP_TINY_API_KEY: Schema.string(), APP_TITLE: Schema.string(), + APP_SENTRY_DSN: Schema.string.optional(), + APP_SENTRY_NORMALIZE_DEPTH: Schema.number.optional(), + APP_SENTRY_TRACES_SAMPLE_RATE: Schema.number.optional(), + APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE: Schema.number.optional(), + APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE: Schema.number.optional(), }) diff --git a/package.json b/package.json index b395422f64..5cc28e80be 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "dependencies": { "@ifrc-go/icons": "^1.2.0", "@mapbox/mapbox-gl-draw": "^1.2.0", + "@sentry/react": "^7.81.1", "@tinymce/tinymce-react": "^4.3.0", "@togglecorp/fujs": "^2.0.0", "@togglecorp/re-map": "^0.2.0-beta-6", diff --git a/src/App/index.tsx b/src/App/index.tsx index 47023617f5..f8d35058ed 100644 --- a/src/App/index.tsx +++ b/src/App/index.tsx @@ -10,6 +10,7 @@ import { } from 'react-router-dom'; import mapboxgl from 'mapbox-gl'; import { isDefined, unique } from '@togglecorp/fujs'; +import * as Sentry from '@sentry/react'; import UserContext, { UserAuth, UserContextProps } from '#contexts/user'; import AlertContext, { AlertParams, AlertContextProps } from '#contexts/alert'; @@ -44,11 +45,11 @@ const requestContextValue = { transformResponse: processGoResponse, transformError: processGoError, }; - -const router = createBrowserRouter(unwrappedRoutes); +const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createBrowserRouter); +const router = sentryCreateBrowserRouter(unwrappedRoutes); mapboxgl.accessToken = mbtoken; -function App() { +function Application() { // ALERTS const [alerts, setAlerts] = useState([]); @@ -242,4 +243,5 @@ function App() { ); } +const App = Sentry.withProfiler(Application); export default App; diff --git a/src/config.ts b/src/config.ts index 408f3c3dd7..5768010c2c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,12 +6,13 @@ const { APP_RISK_API_ENDPOINT, APP_TINY_API_KEY, APP_SHOW_ENV_BANNER, - APP_SENTRY_DSN, - APP_SENTRY_TRACES_SAMPLE_RATE, - APP_SENTRY_NORMALIZE_DEPTH, APP_TITLE, APP_COMMIT_HASH, APP_VERSION, + APP_SENTRY_DSN, + APP_SENTRY_TRACES_SAMPLE_RATE, + APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE, + APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE, } = import.meta.env; export const environment = APP_ENVIRONMENT; @@ -27,5 +28,6 @@ export const showEnvBanner = APP_SHOW_ENV_BANNER; export const riskApi = APP_RISK_API_ENDPOINT; export const tinyApiKey = APP_TINY_API_KEY; export const sentryAppDsn = APP_SENTRY_DSN; -export const sentryTraceSampleRate = APP_SENTRY_TRACES_SAMPLE_RATE; -export const sentryNormalizeDepth = APP_SENTRY_NORMALIZE_DEPTH; +export const sentryTracesSampleRate = APP_SENTRY_TRACES_SAMPLE_RATE; +export const sentryReplaysSessionSampleRate = APP_SENTRY_REPLAYS_SESSION_SAMPLE_RATE; +export const sentryReplaysOnErrorSampleRate = APP_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE; diff --git a/src/index.tsx b/src/index.tsx index 40efe4fb27..6f1c898150 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,12 +1,65 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import { isNotDefined } from '@togglecorp/fujs'; +import { isNotDefined, isDefined } from '@togglecorp/fujs'; +import * as Sentry from '@sentry/react'; +import { + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, +} from 'react-router-dom'; +import { + appTitle, + appCommitHash, + api, + environment, + sentryAppDsn, + sentryReplaysOnErrorSampleRate, + sentryReplaysSessionSampleRate, + sentryTracesSampleRate, +} from '#config'; import 'mapbox-gl/dist/mapbox-gl.css'; import './index.css'; import App from './App/index.tsx'; +if (isDefined(sentryAppDsn)) { + Sentry.init({ + dsn: sentryAppDsn, + release: `${appTitle}@${appCommitHash}`, + environment, + integrations: [ + new Sentry.BrowserTracing({ + routingInstrumentation: Sentry.reactRouterV6Instrumentation( + React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + ), + }), + new Sentry.Replay(), + ], + + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + tracesSampleRate: Number(sentryTracesSampleRate), + + // Set `tracePropagationTargets` to control for which URLs distributed + // tracing should be enabled + tracePropagationTargets: [ + api, + // riskApi, TODO: let's add this once sentry is configured for risk server + ], + + // Capture Replay for (sentryReplaysSessionSampleRate)% of all sessions, + // plus for (sentryReplaysOnErrorSampleRate)% of sessions with an error + replaysSessionSampleRate: Number(sentryReplaysSessionSampleRate), + replaysOnErrorSampleRate: Number(sentryReplaysOnErrorSampleRate), + }); +} + const webappRootId = 'webapp-root'; const webappRootElement = document.getElementById(webappRootId); diff --git a/yarn.lock b/yarn.lock index 57d598fd85..8798f89ef2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -877,6 +877,66 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@sentry-internal/tracing@7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.81.1.tgz#1180365cd8a9e18cb0f92e1ea970161840ec0e2e" + integrity sha512-E5xm27xrLXL10knH2EWDQsQYh5nb4SxxZzJ3sJwDGG9XGKzBdlp20UUhKqx00wixooVX9uCj3e4Jg8SvNB1hKg== + dependencies: + "@sentry/core" "7.81.1" + "@sentry/types" "7.81.1" + "@sentry/utils" "7.81.1" + +"@sentry/browser@7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.81.1.tgz#5ee6ae3679ee80f444d2e8c5662430e7a734ae50" + integrity sha512-DNtS7bZEnFPKVoGazKs5wHoWC0FwsOFOOMNeDvEfouUqKKbjO7+RDHbr7H6Bo83zX4qmZWRBf8V+3n3YPIiJFw== + dependencies: + "@sentry-internal/tracing" "7.81.1" + "@sentry/core" "7.81.1" + "@sentry/replay" "7.81.1" + "@sentry/types" "7.81.1" + "@sentry/utils" "7.81.1" + +"@sentry/core@7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.81.1.tgz#082fd9122bf9a488c8e05b1754724ddbc2d5cf30" + integrity sha512-tU37yAmckOGCw/moWKSwekSCWWJP15O6luIq+u7wal22hE88F3Vc5Avo8SeF3upnPR+4ejaOFH+BJTr6bgrs6Q== + dependencies: + "@sentry/types" "7.81.1" + "@sentry/utils" "7.81.1" + +"@sentry/react@^7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.81.1.tgz#6a94e8e373a5bf27330cea2eb1a603ae0eb0b8ba" + integrity sha512-kk0plP/mf8KgVLOiImIpp1liYysmh3Un8uXcVAToomSuHZPGanelFAdP0XhY+0HlWU9KIfxTjhMte1iSwQ8pYw== + dependencies: + "@sentry/browser" "7.81.1" + "@sentry/types" "7.81.1" + "@sentry/utils" "7.81.1" + hoist-non-react-statics "^3.3.2" + +"@sentry/replay@7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.81.1.tgz#a656d55e2a00b34e42be6eeb79018d21efc223af" + integrity sha512-4ueT0C4bYjngN/9p0fEYH10dTMLovHyk9HxJ6zSTgePvGVexhg+cSEHXisoBDwHeRZVnbIvsVM0NA7rmEDXJJw== + dependencies: + "@sentry-internal/tracing" "7.81.1" + "@sentry/core" "7.81.1" + "@sentry/types" "7.81.1" + "@sentry/utils" "7.81.1" + +"@sentry/types@7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.81.1.tgz#2b2551fc291e1089651fd574a68f7c4175878bd5" + integrity sha512-dvJvGyctiaPMIQqa46k56Re5IODWMDxiHJ1UjBs/WYDLrmWFPGrEbyJ8w8CYLhYA+7qqrCyIZmHbWSTRIxstHw== + +"@sentry/utils@7.81.1": + version "7.81.1" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.81.1.tgz#42f3e77baf90205cec1f8599eb8445a6918030bd" + integrity sha512-gq+MDXIirHKxNZ+c9/lVvCXd6y2zaZANujwlFggRH2u9SRiPaIXVilLpvMm4uJqmqBMEcY81ArujExtHvkbCqg== + dependencies: + "@sentry/types" "7.81.1" + "@svgr/babel-plugin-add-jsx-attribute@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz#80856c1b7a3b7422d232f6e079f0beb90c4a13e9" @@ -4268,6 +4328,13 @@ hat@0.0.3: resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a" integrity sha512-zpImx2GoKXy42fVDSEad2BPKuSQdLcqsCYa48K3zHSzM/ugWuYjLDr8IXxpVuL7uCLHw56eaiLxCRthhOzf5ug== +hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -6209,7 +6276,7 @@ react-focus-on@^3.8.1: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" -react-is@^16.13.1: +react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==