Skip to content

Commit 99df07e

Browse files
authored
POC Sentry (#206)
* POC Sentry * Remove release & use getClientEnv * remove sourcemap from check build
1 parent feb26ae commit 99df07e

File tree

17 files changed

+277
-40
lines changed

17 files changed

+277
-40
lines changed

.github/workflows/check.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ jobs:
2626
- run: pnpm run -r lint
2727
- run: pnpm run -r type-check
2828
- run: pnpm run test:all
29-
- run: pnpm run -r build
29+
- run: pnpm run -r build-no-sourcemaps

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ Thumbs.db
4444

4545
# Firebase
4646
firebase-debug.log
47-
ui-debug.log
47+
ui-debug.log
48+
# Sentry Config File
49+
.sentryclirc

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"@remix-run/node": "^1.19.3",
7474
"@remix-run/react": "^1.19.3",
7575
"@remix-run/serve": "^1.19.3",
76+
"@sentry/remix": "^7.74.0",
7677
"@tanstack/react-table": "^8.10.6",
7778
"@tanstack/react-virtual": "3.0.0-beta.61",
7879
"clsx": "^1.2.1",

packages/app-builder/.env.local

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ SESSION_SECRET=SESSION_SECRET
55
SESSION_MAX_AGE=43200
66

77
MARBLE_API_DOMAIN=http://127.0.0.1:8080
8+
MARBLE_APP_DOMAIN=http://127.0.0.1:3000
89

910
FIREBASE_AUTH_EMULATOR=true
1011
FIREBASE_AUTH_EMULATOR_HOST=http://localhost:9099
@@ -14,4 +15,8 @@ FIREBASE_AUTH_DOMAIN=tokyo-country-381508.firebaseapp.com
1415
FIREBASE_PROJECT_ID=tokyo-country-381508
1516
FIREBASE_STORAGE_BUCKET=tokyo-country-381508.appspot.com
1617
FIREBASE_MESSAGING_SENDER_ID=1047691849054
17-
FIREBASE_APP_ID=1:1047691849054:web:a5b69dd2ac584c1160b3cf
18+
FIREBASE_APP_ID=1:1047691849054:web:a5b69dd2ac584c1160b3cf
19+
20+
SENTRY_AUTH_TOKEN=dummy
21+
SENTRY_DSN=dummy
22+
SENTRY_ENVIRONMENT=development

packages/app-builder/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"lint": "eslint .",
1515
"type-check": "npx tsc --noEmit",
1616
"dev": "dotenv -e .env.local -- remix dev",
17-
"build": "remix build",
17+
"build-no-sourcemaps": "remix build",
18+
"build": "remix build --sourcemap && sentry-upload-sourcemaps --org checkmarble --project marble-frontend",
1819
"start": "remix start",
1920
"generate-routes": "source scripts/generateRoutes.sh"
2021
},

packages/app-builder/src/entry.client.tsx

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { RemixBrowser } from '@remix-run/react';
2-
import { startTransition, StrictMode } from 'react';
1+
import { RemixBrowser, useLocation, useMatches } from '@remix-run/react';
2+
import * as Sentry from '@sentry/remix';
3+
import { startTransition, StrictMode, useEffect } from 'react';
34
import { hydrateRoot } from 'react-dom/client';
45
import { I18nextProvider } from 'react-i18next';
56

67
import { clientServices } from './services/init.client';
8+
import { getClientEnv } from './utils/environment.client';
79

810
async function hydrate() {
911
const { i18nextClientService } = clientServices;
@@ -30,3 +32,35 @@ if (window.requestIdleCallback) {
3032
// eslint-disable-next-line @typescript-eslint/no-misused-promises
3133
window.setTimeout(hydrate, 1);
3234
}
35+
36+
Sentry.init({
37+
dsn: getClientEnv('SENTRY_DSN'),
38+
environment: getClientEnv('SENTRY_ENVIRONMENT'),
39+
integrations: [
40+
new Sentry.BrowserTracing({
41+
routingInstrumentation: Sentry.remixRouterInstrumentation(
42+
useEffect,
43+
useLocation,
44+
useMatches
45+
),
46+
}),
47+
// Replay is only available in the client
48+
new Sentry.Replay(),
49+
],
50+
51+
// Set tracesSampleRate to 1.0 to capture 100%
52+
// of transactions for performance monitoring.
53+
// We recommend adjusting this value in production
54+
tracesSampleRate: 1.0,
55+
56+
// Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
57+
tracePropagationTargets: [
58+
getClientEnv('MARBLE_APP_DOMAIN'),
59+
getClientEnv('MARBLE_API_DOMAIN'),
60+
],
61+
62+
// Capture Replay for 10% of all sessions,
63+
// plus for 100% of sessions with an error
64+
replaysSessionSampleRate: 0.1,
65+
replaysOnErrorSampleRate: 1.0,
66+
});

packages/app-builder/src/entry.server.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
import { type EntryContext, Response } from '@remix-run/node';
1+
import {
2+
type DataFunctionArgs,
3+
type EntryContext,
4+
Response,
5+
} from '@remix-run/node';
26
import { RemixServer } from '@remix-run/react';
7+
import * as Sentry from '@sentry/remix';
38
import isbot from 'isbot';
49
import { renderToPipeableStream } from 'react-dom/server';
510
import { I18nextProvider } from 'react-i18next';
611
import { PassThrough } from 'stream';
712

813
import { serverServices } from './services/init.server';
14+
import { getServerEnv } from './utils/environment.server';
915

1016
const ABORT_DELAY = 5000;
1117

@@ -105,3 +111,24 @@ function handleBrowserRequest(
105111
setTimeout(abort, ABORT_DELAY);
106112
});
107113
}
114+
115+
Sentry.init({
116+
dsn: getServerEnv('SENTRY_DSN'),
117+
environment: getServerEnv('SENTRY_ENVIRONMENT'),
118+
// Set tracesSampleRate to 1.0 to capture 100%
119+
// of transactions for performance monitoring.
120+
// We recommend adjusting this value in production
121+
tracesSampleRate: 1.0,
122+
});
123+
124+
export async function handleError(
125+
error: unknown,
126+
{ request }: DataFunctionArgs
127+
) {
128+
if (error instanceof Error) {
129+
await Sentry.captureRemixServerException(error, 'remix.server', request);
130+
} else {
131+
// Optionally capture non-Error objects
132+
Sentry.captureException(error);
133+
}
134+
}

packages/app-builder/src/root.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
useLoaderData,
1515
useRouteError,
1616
} from '@remix-run/react';
17+
import { captureRemixErrorBoundaryError, withSentry } from '@sentry/remix';
1718
import { Tooltip } from '@ui-design-system';
1819
import { LogoStandard } from '@ui-icons';
1920
import { type Namespace } from 'i18next';
@@ -101,6 +102,7 @@ export const meta: V2_MetaFunction = () => [
101102

102103
export function ErrorBoundary() {
103104
const error = useRouteError();
105+
captureRemixErrorBoundaryError(error);
104106

105107
return (
106108
<html lang="en">
@@ -135,7 +137,7 @@ export function ErrorBoundary() {
135137
);
136138
}
137139

138-
export default function App() {
140+
function App() {
139141
const { locale, ENV, toastMessage, csrf } = useLoaderData<typeof loader>();
140142

141143
const { i18n } = useTranslation(handle.i18n);
@@ -169,3 +171,5 @@ export default function App() {
169171
</html>
170172
);
171173
}
174+
175+
export default withSentry(App);

packages/app-builder/src/routes/__builder/decisions.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import { DecisionsPage } from '@app-builder/components/Decisions';
77
import { getRoute } from '@app-builder/utils/routes';
88
import { Outlet, useRouteError } from '@remix-run/react';
9+
import { captureRemixErrorBoundaryError } from '@sentry/remix';
910
import { Decision } from '@ui-icons';
1011
import { type Namespace } from 'i18next';
1112
import { useTranslation } from 'react-i18next';
@@ -52,5 +53,8 @@ export default function DecisionsLayout() {
5253
}
5354

5455
export function ErrorBoundary() {
55-
return <ErrorComponent error={useRouteError()} />;
56+
const error = useRouteError();
57+
captureRemixErrorBoundaryError(error);
58+
59+
return <ErrorComponent error={error} />;
5660
}

packages/app-builder/src/routes/__builder/lists/$listId.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { serverServices } from '@app-builder/services/init.server';
1212
import { fromParams } from '@app-builder/utils/short-uuid';
1313
import { json, type LoaderArgs } from '@remix-run/node';
1414
import { Link, useLoaderData, useRouteError } from '@remix-run/react';
15+
import { captureRemixErrorBoundaryError } from '@sentry/remix';
1516
import {
1617
type ColumnDef,
1718
getCoreRowModel,
@@ -151,9 +152,10 @@ export default function Lists() {
151152
</Page.Container>
152153
);
153154
}
154-
155155
export function ErrorBoundary() {
156-
return <ErrorComponent error={useRouteError()} />;
156+
const error = useRouteError();
157+
captureRemixErrorBoundaryError(error);
158+
return <ErrorComponent error={error} />;
157159
}
158160

159161
// Correspond to this part of the UI : https://www.figma.com/file/JW6QvnhBtdZDcKvLdg9s5T/Marble-Portal?node-id=6377%3A53150&mode=dev

packages/app-builder/src/routes/__builder/lists/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getRoute } from '@app-builder/utils/routes';
66
import { fromUUID } from '@app-builder/utils/short-uuid';
77
import { json, type LoaderArgs } from '@remix-run/node';
88
import { useLoaderData, useNavigate, useRouteError } from '@remix-run/react';
9+
import { captureRemixErrorBoundaryError } from '@sentry/remix';
910
import {
1011
type ColumnDef,
1112
getCoreRowModel,
@@ -114,7 +115,8 @@ export default function ListsPage() {
114115
</Page.Container>
115116
);
116117
}
117-
118118
export function ErrorBoundary() {
119-
return <ErrorComponent error={useRouteError()} />;
119+
const error = useRouteError();
120+
captureRemixErrorBoundaryError(error);
121+
return <ErrorComponent error={error} />;
120122
}

packages/app-builder/src/routes/__builder/scenarios/$scenarioId.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { serverServices } from '@app-builder/services/init.server';
33
import { fromParams } from '@app-builder/utils/short-uuid';
44
import { json, type LoaderArgs, type SerializeFrom } from '@remix-run/node';
55
import { Outlet, useRouteError, useRouteLoaderData } from '@remix-run/react';
6+
import { captureRemixErrorBoundaryError } from '@sentry/remix';
67
import { type Namespace } from 'i18next';
78

89
export const handle = {
@@ -30,7 +31,8 @@ export const useCurrentScenario = () =>
3031
export default function CurrentScenarioProvider() {
3132
return <Outlet />;
3233
}
33-
3434
export function ErrorBoundary() {
35-
return <ErrorComponent error={useRouteError()} />;
35+
const error = useRouteError();
36+
captureRemixErrorBoundaryError(error);
37+
return <ErrorComponent error={error} />;
3638
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useFetcher } from '@remix-run/react';
2+
import { Button } from '@ui-design-system';
3+
4+
export function loader() {
5+
return 'ok';
6+
}
7+
8+
export function action() {
9+
throw new Error('This is an error from an action');
10+
}
11+
12+
export default function Sentry() {
13+
const fetcher = useFetcher<typeof action>();
14+
const iStillDoNotExist = () => {
15+
throw new Error('This is an error from a component');
16+
};
17+
return (
18+
<div>
19+
<h1>Test Sentry</h1>
20+
<Button onClick={() => iStillDoNotExist()}>
21+
Crash the app from the browser
22+
</Button>
23+
<fetcher.Form method="POST" action="/sentry">
24+
<Button type="submit">Crash the app with a server error</Button>
25+
</fetcher.Form>
26+
</div>
27+
);
28+
}

packages/app-builder/src/utils/environment.client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ export type ClientEnvVars = {
1111
AUTH_EMULATOR_HOST?: string;
1212
FIREBASE_OPTIONS: FirebaseOptions;
1313
MARBLE_API_DOMAIN: string;
14+
MARBLE_APP_DOMAIN: string;
15+
SENTRY_DSN: string;
16+
SENTRY_ENVIRONMENT: string;
1417
};
1518

1619
export function getClientEnv<K extends keyof ClientEnvVars>(

packages/app-builder/src/utils/environment.server.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,18 @@ type ServerPublicEnvVarName =
3434
| 'FIREBASE_PROJECT_ID'
3535
| 'FIREBASE_STORAGE_BUCKET'
3636
| 'MARBLE_API_DOMAIN'
37+
| 'MARBLE_APP_DOMAIN'
3738
| 'NODE_ENV'
39+
| 'SENTRY_ENVIRONMENT'
3840
| 'SESSION_MAX_AGE';
3941

4042
/**
4143
* List of all secret env vars to defined on each deployed environments
4244
*/
43-
type ServerSecretEnvVarName = 'SESSION_SECRET';
45+
type ServerSecretEnvVarName =
46+
| 'SENTRY_AUTH_TOKEN'
47+
| 'SENTRY_DSN'
48+
| 'SESSION_SECRET';
4449

4550
type ServerEnvVarName =
4651
| DevServerEnvVarName
@@ -84,5 +89,8 @@ export function getClientEnvVars(): ClientEnvVars {
8489
appId: getServerEnv('FIREBASE_APP_ID'),
8590
},
8691
MARBLE_API_DOMAIN: getServerEnv('MARBLE_API_DOMAIN'),
92+
MARBLE_APP_DOMAIN: getServerEnv('MARBLE_APP_DOMAIN'),
93+
SENTRY_DSN: getServerEnv('SENTRY_DSN'),
94+
SENTRY_ENVIRONMENT: getServerEnv('SENTRY_ENVIRONMENT'),
8795
};
8896
}

packages/app-builder/src/utils/routes/routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ export const routes = [
229229
}
230230
]
231231
},
232+
{
233+
"id": "routes/sentry",
234+
"path": "sentry",
235+
"file": "routes/sentry.tsx"
236+
},
232237
{
233238
"id": "routes/index",
234239
"index": true,

0 commit comments

Comments
 (0)