|
@@ -0,0 +1,244 @@
|
|
|
+import ExternalLink from 'sentry/components/links/externalLink';
|
|
|
+import {Layout, LayoutProps} from 'sentry/components/onboarding/gettingStartedDoc/layout';
|
|
|
+import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
|
|
|
+import {ProductSolution} from 'sentry/components/onboarding/productSelection';
|
|
|
+import {t, tct} from 'sentry/locale';
|
|
|
+
|
|
|
+// Configuration Start
|
|
|
+const replayIntegration = `
|
|
|
+new Sentry.Replay(),
|
|
|
+`;
|
|
|
+
|
|
|
+const replayOtherConfig = `
|
|
|
+// Session Replay
|
|
|
+replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
|
|
+replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
|
|
+`;
|
|
|
+
|
|
|
+const performanceIntegration = `
|
|
|
+new Sentry.BrowserTracing({
|
|
|
+ // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
|
|
|
+ tracePropagationTargets: ["localhost", "https:yourserver.io/api/"],
|
|
|
+ routingInstrumentation: Sentry.remixRouterInstrumentation(
|
|
|
+ useEffect,
|
|
|
+ useLocation,
|
|
|
+ useMatches
|
|
|
+ ),
|
|
|
+}),
|
|
|
+`;
|
|
|
+
|
|
|
+const performanceOtherConfig = `
|
|
|
+// Performance Monitoring
|
|
|
+tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production!
|
|
|
+`;
|
|
|
+
|
|
|
+const prismaConfig = `
|
|
|
+new Sentry.Integrations.Prisma({ client: prisma })
|
|
|
+`;
|
|
|
+
|
|
|
+export const steps = ({
|
|
|
+ sentryInitContent,
|
|
|
+ sentryInitContentServer,
|
|
|
+}: {
|
|
|
+ sentryInitContent?: string;
|
|
|
+ sentryInitContentServer?: string[];
|
|
|
+} = {}): LayoutProps['steps'] => [
|
|
|
+ {
|
|
|
+ language: 'bash',
|
|
|
+ type: StepType.INSTALL,
|
|
|
+ configurations: [
|
|
|
+ {
|
|
|
+ description: t(
|
|
|
+ 'Sentry captures data by using an SDK within your application’s runtime.'
|
|
|
+ ),
|
|
|
+ code: `
|
|
|
+ # Using yarn
|
|
|
+ yarn add @sentry/remix
|
|
|
+
|
|
|
+ # Using npm
|
|
|
+ npm install --save @sentry/remix
|
|
|
+ `,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ language: 'javascript',
|
|
|
+ type: StepType.CONFIGURE,
|
|
|
+ configurations: [
|
|
|
+ {
|
|
|
+ description: t(
|
|
|
+ 'Import and initialize Sentry in your Remix entry points for both the client and server:'
|
|
|
+ ),
|
|
|
+ code: `
|
|
|
+ import { useLocation, useMatches } from "@remix-run/react";
|
|
|
+ import * as Sentry from "@sentry/remix";
|
|
|
+ import { useEffect } from "react";
|
|
|
+
|
|
|
+ Sentry.init({
|
|
|
+ ${sentryInitContent}
|
|
|
+ });
|
|
|
+ `,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ description: tct(
|
|
|
+ `Initialize Sentry in your entry point for the server to capture exceptions and get performance metrics for your [action] and [loader] functions. You can also initialize Sentry's database integrations, such as Prisma, to get spans for your database calls:`,
|
|
|
+ {
|
|
|
+ action: (
|
|
|
+ <ExternalLink href="https://remix.run/docs/en/v1/api/conventions#action" />
|
|
|
+ ),
|
|
|
+ loader: (
|
|
|
+ <ExternalLink href="https://remix.run/docs/en/1.18.1/api/conventions#loader" />
|
|
|
+ ),
|
|
|
+ }
|
|
|
+ ),
|
|
|
+ code: `
|
|
|
+
|
|
|
+ ${
|
|
|
+ (sentryInitContentServer ?? []).length > 1
|
|
|
+ ? `import { prisma } from "~/db.server";`
|
|
|
+ : ''
|
|
|
+ }
|
|
|
+
|
|
|
+ import * as Sentry from "@sentry/remix";
|
|
|
+
|
|
|
+ Sentry.init({
|
|
|
+ ${sentryInitContentServer?.join('\n')}
|
|
|
+ });
|
|
|
+ `,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ description: t(
|
|
|
+ 'Lastly, wrap your Remix root with "withSentry" to catch React component errors and to get parameterized router transactions:'
|
|
|
+ ),
|
|
|
+ code: `
|
|
|
+import {
|
|
|
+ Links,
|
|
|
+ LiveReload,
|
|
|
+ Meta,
|
|
|
+ Outlet,
|
|
|
+ Scripts,
|
|
|
+ ScrollRestoration,
|
|
|
+} from "@remix-run/react";
|
|
|
+
|
|
|
+import { withSentry } from "@sentry/remix";
|
|
|
+
|
|
|
+function App() {
|
|
|
+ return (
|
|
|
+ <html>
|
|
|
+ <head>
|
|
|
+ <Meta />
|
|
|
+ <Links />
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+ <Outlet />
|
|
|
+ <ScrollRestoration />
|
|
|
+ <Scripts />
|
|
|
+ <LiveReload />
|
|
|
+ </body>
|
|
|
+ </html>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default withSentry(App);
|
|
|
+ `,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ language: 'javascript',
|
|
|
+ type: StepType.VERIFY,
|
|
|
+ configurations: [
|
|
|
+ {
|
|
|
+ description: t(
|
|
|
+ "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
|
|
|
+ ),
|
|
|
+ code: `
|
|
|
+ return <button onClick={() => methodDoesNotExist()}>Break the world</button>;
|
|
|
+ `,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+export const nextSteps = [
|
|
|
+ {
|
|
|
+ id: 'source-maps',
|
|
|
+ name: t('Source Maps'),
|
|
|
+ description: t('Learn how to enable readable stack traces in your Sentry errors.'),
|
|
|
+ link: 'https://docs.sentry.io/platforms/javascript/guides/remix/sourcemaps/',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'performance-monitoring',
|
|
|
+ name: t('Performance Monitoring'),
|
|
|
+ description: t(
|
|
|
+ 'Track down transactions to connect the dots between 10-second page loads and poor-performing API calls or slow database queries.'
|
|
|
+ ),
|
|
|
+ link: 'https://docs.sentry.io/platforms/javascript/guides/remix/performance/',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'session-replay',
|
|
|
+ name: t('Session Replay'),
|
|
|
+ description: t(
|
|
|
+ 'Get to the root cause of an error or latency issue faster by seeing all the technical details related to that issue in one visual replay on your web application.'
|
|
|
+ ),
|
|
|
+ link: 'https://docs.sentry.io/platforms/javascript/guides/remix/session-replay/',
|
|
|
+ },
|
|
|
+];
|
|
|
+// Configuration End
|
|
|
+
|
|
|
+type Props = {
|
|
|
+ activeProductSelection: ProductSolution[];
|
|
|
+ dsn: string;
|
|
|
+ newOrg?: boolean;
|
|
|
+};
|
|
|
+
|
|
|
+export default function GettingStartedWithRemix({
|
|
|
+ dsn,
|
|
|
+ activeProductSelection,
|
|
|
+ newOrg,
|
|
|
+}: Props) {
|
|
|
+ const integrations: string[] = [];
|
|
|
+ const otherConfigs: string[] = [];
|
|
|
+
|
|
|
+ let nextStepDocs = [...nextSteps];
|
|
|
+ const sentryInitContentServer: string[] = [`dsn: "${dsn}",`];
|
|
|
+
|
|
|
+ if (activeProductSelection.includes(ProductSolution.PERFORMANCE_MONITORING)) {
|
|
|
+ integrations.push(performanceIntegration.trim());
|
|
|
+ otherConfigs.push(performanceOtherConfig.trim());
|
|
|
+ sentryInitContentServer.push(prismaConfig.trim());
|
|
|
+ sentryInitContentServer.push(performanceOtherConfig.trim());
|
|
|
+ nextStepDocs = nextStepDocs.filter(
|
|
|
+ step => step.id !== ProductSolution.PERFORMANCE_MONITORING
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (activeProductSelection.includes(ProductSolution.SESSION_REPLAY)) {
|
|
|
+ integrations.push(replayIntegration.trim());
|
|
|
+ otherConfigs.push(replayOtherConfig.trim());
|
|
|
+ nextStepDocs = nextStepDocs.filter(
|
|
|
+ step => step.id !== ProductSolution.SESSION_REPLAY
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ let sentryInitContent: string[] = [`dsn: "${dsn}",`];
|
|
|
+
|
|
|
+ if (integrations.length > 0) {
|
|
|
+ sentryInitContent = sentryInitContent.concat('integrations: [', integrations, '],');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (otherConfigs.length > 0) {
|
|
|
+ sentryInitContent = sentryInitContent.concat(otherConfigs);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Layout
|
|
|
+ steps={steps({
|
|
|
+ sentryInitContent: sentryInitContent.join('\n'),
|
|
|
+ sentryInitContentServer,
|
|
|
+ })}
|
|
|
+ nextSteps={nextStepDocs}
|
|
|
+ newOrg={newOrg}
|
|
|
+ />
|
|
|
+ );
|
|
|
+}
|