From 26f9c7500199a2de6c6f5af34d79f320264a1ad4 Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Mon, 3 Nov 2025 12:34:58 +0100 Subject: [PATCH 1/4] ref(onboarding): Split React onboarding docs --- .../gettingStartedDocs/javascript/react.tsx | 669 ------------------ .../javascript/react/crashReport.tsx | 34 + .../javascript/react/feedback.tsx | 64 ++ .../javascript/react/index.tsx | 23 + .../javascript/react/logs.tsx | 9 + .../javascript/react/onboarding.spec.tsx | 121 ++++ .../javascript/react/onboarding.tsx | 277 ++++++++ .../javascript/react/performance.tsx | 119 ++++ .../javascript/react/profiling.tsx | 9 + .../javascript/react/replay.tsx | 52 ++ .../javascript/react/utils.tsx | 119 ++++ 11 files changed, 827 insertions(+), 669 deletions(-) delete mode 100644 static/app/gettingStartedDocs/javascript/react.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/crashReport.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/feedback.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/index.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/logs.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/onboarding.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/performance.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/profiling.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/replay.tsx create mode 100644 static/app/gettingStartedDocs/javascript/react/utils.tsx diff --git a/static/app/gettingStartedDocs/javascript/react.tsx b/static/app/gettingStartedDocs/javascript/react.tsx deleted file mode 100644 index 487967bdd210ee..00000000000000 --- a/static/app/gettingStartedDocs/javascript/react.tsx +++ /dev/null @@ -1,669 +0,0 @@ -import {ExternalLink} from 'sentry/components/core/link'; -import {buildSdkConfig} from 'sentry/components/onboarding/gettingStartedDoc/buildSdkConfig'; -import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout'; -import {widgetCalloutBlock} from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; -import {tracePropagationBlock} from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; -import type { - ContentBlock, - Docs, - DocsParams, - OnboardingConfig, -} from 'sentry/components/onboarding/gettingStartedDoc/types'; -import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; -import { - getAIRulesForCodeEditorStep, - getUploadSourceMapsStep, -} from 'sentry/components/onboarding/gettingStartedDoc/utils'; -import { - getCrashReportJavaScriptInstallSteps, - getCrashReportModalConfigDescription, - getCrashReportModalIntroduction, - getFeedbackConfigOptions, - getFeedbackConfigureDescription, -} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; -import { - getReplayConfigOptions, - getReplayConfigureDescription, - getReplayVerifyStep, -} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding'; -import {featureFlagOnboarding} from 'sentry/gettingStartedDocs/javascript/javascript'; -import {t, tct} from 'sentry/locale'; -import { - getJavascriptLogsOnboarding, - getJavascriptProfilingOnboarding, -} from 'sentry/utils/gettingStartedDocs/javascript'; - -type Params = DocsParams; - -const getDynamicParts = (params: Params): string[] => { - const dynamicParts: string[] = []; - - if (params.isPerformanceSelected) { - dynamicParts.push(` - // Tracing - tracesSampleRate: 1.0, // Capture 100% of the transactions - // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]`); - } - - if (params.isReplaySelected) { - dynamicParts.push(` - // 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.`); - } - - if (params.isLogsSelected) { - dynamicParts.push(` - // Enable logs to be sent to Sentry - enableLogs: true`); - } - - if (params.isProfilingSelected) { - dynamicParts.push(` - // Set profilesSampleRate to 1.0 to profile every transaction. - // Since profilesSampleRate is relative to tracesSampleRate, - // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate - // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would - // results in 25% of transactions being profiled (0.5*0.5=0.25) - profilesSampleRate: 1.0`); - } - - return dynamicParts; -}; - -const getIntegrations = (params: Params): string[] => { - const integrations = []; - if (params.isPerformanceSelected) { - integrations.push(`Sentry.browserTracingIntegration()`); - } - - if (params.isProfilingSelected) { - integrations.push(`Sentry.browserProfilingIntegration()`); - } - - if (params.isReplaySelected) { - integrations.push( - `Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)})` - ); - } - - if (params.isFeedbackSelected) { - integrations.push(` - Sentry.feedbackIntegration({ - colorScheme: "system", - ${getFeedbackConfigOptions(params.feedbackOptions)} - }),`); - } - - return integrations; -}; - -const getSdkSetupSnippet = (params: Params) => { - const config = buildSdkConfig({ - params, - staticParts: [ - `dsn: "${params.dsn.public}"`, - `// Setting this option to true will send default PII data to Sentry. - // For example, automatic IP address collection on events - sendDefaultPii: true`, - ], - getIntegrations, - getDynamicParts, - }); - - return ` -import * as Sentry from "@sentry/react"; - -Sentry.init({ - ${config} -}); - -const container = document.getElementById(“app”); -const root = createRoot(container); -root.render(); -`; -}; - -const getVerifySnippet = (params: Params) => { - const logsCode = params.isLogsSelected - ? ` - // Send a log before throwing the error - Sentry.logger.info('User triggered test error', { - action: 'test_error_button_click', - });` - : ''; - - return `import * as Sentry from '@sentry/react'; -// Add this button component to your app to test Sentry's error tracking -function ErrorButton() { - return ( - - ); -}`; -}; - -const installSnippetBlock: ContentBlock = { - type: 'code', - tabs: [ - { - label: 'npm', - language: 'bash', - code: 'npm install --save @sentry/react', - }, - { - label: 'yarn', - language: 'bash', - code: 'yarn add @sentry/react', - }, - { - label: 'pnpm', - language: 'bash', - code: 'pnpm add @sentry/react', - }, - ], -}; - -const onboarding: OnboardingConfig = { - introduction: () => - tct( - "In this quick guide you'll use [strong:npm], [strong:yarn], or [strong:pnpm] to set up:", - { - strong: , - } - ), - install: () => [ - { - type: StepType.INSTALL, - content: [ - { - type: 'text', - text: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', - {code: } - ), - }, - installSnippetBlock, - ], - }, - ], - configure: (params: Params) => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: t( - "Initialize Sentry as early as possible in your application's lifecycle." - ), - }, - { - type: 'code', - tabs: [ - { - label: 'JavaScript', - language: 'javascript', - code: getSdkSetupSnippet(params), - }, - ], - }, - { - type: 'conditional', - condition: params.isReplaySelected, - content: [tracePropagationBlock], - }, - ], - }, - getUploadSourceMapsStep({ - guideLink: 'https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/', - ...params, - }), - getAIRulesForCodeEditorStep({ - // ATTENTION: The rules defined here must match those in the documentation (see: https://github.com/getsentry/sentry-docs/blob/master/platform-includes/llm-rules-logs/javascript.react.mdx). - // If you make any changes, please update the docs accordingly. - rules: ` -These examples should be used as guidance when configuring Sentry functionality within a project. - -# Error / Exception Tracking - -Use \`Sentry.captureException(error)\` to capture an exception and log the error in Sentry. -Use this in try catch blocks or areas where exceptions are expected - -# Tracing Examples - -Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls -Ensure you are creating custom spans with meaningful names and operations -Use the \`Sentry.startSpan\` function to create a span -Child spans can exist within a parent span - -## Custom Span instrumentation in component actions - -\`\`\`javascript -function TestComponent() { - const handleTestButtonClick = () => { - // Create a transaction/span to measure performance - Sentry.startSpan( - { - op: "ui.click", - name: "Test Button Click", - }, - (span) => { - const value = "some config"; - const metric = "some metric"; - - // Metrics can be added to the span - span.setAttribute("config", value); - span.setAttribute("metric", metric); - - doSomething(); - }, - ); - }; - - return ( - - ); -} -\`\`\` - -## Custom span instrumentation in API calls - -\`\`\`javascript -async function fetchUserData(userId) { - return Sentry.startSpan( - { - op: "http.client", - name: \`GET /api/users/\${userId}\`, - }, - async () => { - const response = await fetch(\`/api/users/\${userId}\`); - const data = await response.json(); - return data; - }, - ); -} -\`\`\` - -# Logs - -Where logs are used, ensure Sentry is imported using \`import * as Sentry from "@sentry/react"\` -Enable logging in Sentry using \`Sentry.init({ enableLogs: true })\` -Reference the logger using \`const { logger } = Sentry\` -Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls - -## Configuration - -### Baseline - -\`\`\`javascript -import * as Sentry from "@sentry/react"; - -Sentry.init({ - dsn: "${params.dsn.public}", - - enableLogs: true, -}); -\`\`\` - -### Logger Integration - -\`\`\`javascript -Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [ - // send console.log, console.warn, and console.error calls as logs to Sentry - Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }), - ], -}); -\`\`\` - -## Logger Examples - -\`logger.fmt\` is a template literal function that should be used to bring variables into the structured logs. - -\`\`\`javascript -logger.trace("Starting database connection", { database: "users" }); -logger.debug(logger.fmt\`Cache miss for user: \${userId}\`); -logger.info("Updated profile", { profileId: 345 }); -logger.warn("Rate limit reached for endpoint", { - endpoint: "/api/results/", - isEnterprise: false, -}); -logger.error("Failed to process payment", { - orderId: "order_123", - amount: 99.99, -}); -logger.fatal("Database connection pool exhausted", { - database: "users", - activeConnections: 100, -}); -\`\`\` -`, - }), - ], - verify: (params: Params) => [ - { - type: StepType.VERIFY, - content: [ - { - type: 'text', - text: t( - "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." - ), - }, - { - type: 'code', - tabs: [ - { - label: 'React', - language: 'javascript', - code: getVerifySnippet(params), - }, - ], - }, - ], - }, - ], - nextSteps: (params: Params) => { - const steps = [ - { - id: 'react-features', - name: t('React Features'), - description: t( - 'Learn about our first class integration with the React framework.' - ), - link: 'https://docs.sentry.io/platforms/javascript/guides/react/features/', - }, - ]; - - if (params.isPerformanceSelected) { - steps.push({ - id: 'react-router', - name: t('React Router'), - description: t( - 'Configure routing, so Sentry can generate parameterized route names for better grouping of tracing data.' - ), - link: 'https://docs.sentry.io/platforms/javascript/guides/react/configuration/integrations/react-router/', - }); - } - - if (params.isLogsSelected) { - steps.push({ - id: 'logs', - name: t('Logging Integrations'), - description: t( - 'Add logging integrations to automatically capture logs from your application.' - ), - link: 'https://docs.sentry.io/platforms/javascript/guides/react/logs/#integrations', - }); - } - - return steps; - }, -}; - -const replayOnboarding: OnboardingConfig = { - install: () => [ - { - type: StepType.INSTALL, - content: [ - { - type: 'text', - text: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]. You need a minimum version 7.27.0 of [code:@sentry/react] in order to use Session Replay. You do not need to install any additional packages.', - {code: } - ), - }, - installSnippetBlock, - ], - }, - ], - configure: (params: Params) => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: getReplayConfigureDescription({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/session-replay/', - }), - }, - { - type: 'code', - tabs: [ - { - label: 'JavaScript', - language: 'javascript', - code: getSdkSetupSnippet(params), - }, - ], - }, - ], - }, - ], - verify: getReplayVerifyStep(), - nextSteps: () => [], -}; - -const feedbackOnboarding: OnboardingConfig = { - install: () => [ - { - type: StepType.INSTALL, - content: [ - { - type: 'text', - text: tct( - 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/react]) installed, minimum version 7.85.0.', - { - code: , - } - ), - }, - installSnippetBlock, - ], - }, - ], - configure: (params: Params) => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: getFeedbackConfigureDescription({ - linkConfig: - 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/', - linkButton: - 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#bring-your-own-button', - }), - }, - { - type: 'code', - tabs: [ - { - label: 'JavaScript', - language: 'javascript', - code: getSdkSetupSnippet(params), - }, - ], - }, - { - type: 'custom', - content: crashReportCallout({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#crash-report-modal', - }), - }, - ], - }, - ], - verify: () => [], - nextSteps: () => [], -}; - -const crashReportOnboarding: OnboardingConfig = { - introduction: () => getCrashReportModalIntroduction(), - install: (params: Params) => getCrashReportJavaScriptInstallSteps(params), - configure: () => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: getCrashReportModalConfigDescription({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#crash-report-modal', - }), - }, - widgetCalloutBlock({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#user-feedback-widget', - }), - ], - }, - ], - verify: () => [], - nextSteps: () => [], -}; - -const performanceOnboarding: OnboardingConfig = { - introduction: () => - t( - "Adding Performance to your React project is simple. Make sure you've got these basics down." - ), - install: onboarding.install, - configure: params => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: t( - "Configuration should happen as early as possible in your application's lifecycle." - ), - }, - { - type: 'code', - language: 'javascript', - code: ` -import React from "react"; -import ReactDOM from "react-dom"; -import * as Sentry from "@sentry/react"; -import App from "./App"; - -Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [Sentry.browserTracingIntegration()], - - // Set tracesSampleRate to 1.0 to capture 100% - // of transactions for performance monitoring. - // We recommend adjusting this value in production - tracesSampleRate: 1.0, - // Set \`tracePropagationTargets\` to control for which URLs distributed tracing should be enabled - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/], -}); - -ReactDOM.render(, document.getElementById("root")); - -// Can also use with React Concurrent Mode -// ReactDOM.createRoot(document.getElementById('root')).render(); -`, - }, - { - type: 'text', - text: tct( - 'We recommend adjusting the value of [code:tracesSampleRate] in production. Learn more about tracing [linkTracingOptions:options], how to use the [linkTracesSampler:traces_sampler] function, or how to do [linkSampleTransactions:sampling].', - { - code: , - linkTracingOptions: ( - - ), - linkTracesSampler: ( - - ), - linkSampleTransactions: ( - - ), - } - ), - }, - ], - }, - { - title: t('Add Distributed Tracing (Optional)'), - content: [ - { - type: 'text', - text: tct( - "If you're using the current version of our JavaScript SDK and have enabled the [code: BrowserTracing] integration, distributed tracing will work out of the box. To get around possible [link:Browser CORS] issues, define your [code:tracePropagationTargets].", - { - code: , - link: ( - - ), - } - ), - }, - { - type: 'code', - language: 'javascript', - code: ` -Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [Sentry.browserTracingIntegration()], - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/] -}); -`, - }, - ], - }, - ], - verify: () => [ - { - type: StepType.VERIFY, - content: [ - { - type: 'text', - text: tct( - 'Verify that performance monitoring is working correctly with our [link:automatic instrumentation] by simply using your React application.', - { - link: ( - - ), - } - ), - }, - ], - }, - ], - nextSteps: () => [], -}; - -const profilingOnboarding = getJavascriptProfilingOnboarding({ - installSnippetBlock, - docsLink: - 'https://docs.sentry.io/platforms/javascript/guides/react/profiling/browser-profiling/', -}); - -const logsOnboarding: OnboardingConfig = getJavascriptLogsOnboarding({ - installSnippetBlock, - docsPlatform: 'react', - packageName: '@sentry/react', -}); - -const docs: Docs = { - onboarding, - feedbackOnboardingNpm: feedbackOnboarding, - replayOnboarding, - performanceOnboarding, - crashReportOnboarding, - profilingOnboarding, - logsOnboarding, - featureFlagOnboarding, -}; - -export default docs; diff --git a/static/app/gettingStartedDocs/javascript/react/crashReport.tsx b/static/app/gettingStartedDocs/javascript/react/crashReport.tsx new file mode 100644 index 00000000000000..69b06d86c1875c --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/crashReport.tsx @@ -0,0 +1,34 @@ +import {widgetCalloutBlock} from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; +import type { + DocsParams, + OnboardingConfig, +} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import { + getCrashReportJavaScriptInstallSteps, + getCrashReportModalConfigDescription, + getCrashReportModalIntroduction, +} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; + +export const crashReport: OnboardingConfig = { + introduction: () => getCrashReportModalIntroduction(), + install: (params: DocsParams) => getCrashReportJavaScriptInstallSteps(params), + configure: () => [ + { + type: StepType.CONFIGURE, + content: [ + { + type: 'text', + text: getCrashReportModalConfigDescription({ + link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#crash-report-modal', + }), + }, + widgetCalloutBlock({ + link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#user-feedback-widget', + }), + ], + }, + ], + verify: () => [], + nextSteps: () => [], +}; diff --git a/static/app/gettingStartedDocs/javascript/react/feedback.tsx b/static/app/gettingStartedDocs/javascript/react/feedback.tsx new file mode 100644 index 00000000000000..e4e723ab75f43b --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/feedback.tsx @@ -0,0 +1,64 @@ +import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout'; +import type { + DocsParams, + OnboardingConfig, +} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {getFeedbackConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; +import {tct} from 'sentry/locale'; + +import {getSdkSetupSnippet, installSnippetBlock} from './utils'; + +export const feedback: OnboardingConfig = { + install: () => [ + { + type: StepType.INSTALL, + content: [ + { + type: 'text', + text: tct( + 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/react]) installed, minimum version 7.85.0.', + { + code: , + } + ), + }, + installSnippetBlock, + ], + }, + ], + configure: (params: DocsParams) => [ + { + type: StepType.CONFIGURE, + content: [ + { + type: 'text', + text: getFeedbackConfigureDescription({ + linkConfig: + 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/', + linkButton: + 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#bring-your-own-button', + }), + }, + { + type: 'code', + tabs: [ + { + label: 'JavaScript', + language: 'javascript', + code: getSdkSetupSnippet(params), + }, + ], + }, + { + type: 'custom', + content: crashReportCallout({ + link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#crash-report-modal', + }), + }, + ], + }, + ], + verify: () => [], + nextSteps: () => [], +}; diff --git a/static/app/gettingStartedDocs/javascript/react/index.tsx b/static/app/gettingStartedDocs/javascript/react/index.tsx new file mode 100644 index 00000000000000..b8d2f35b78df91 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/index.tsx @@ -0,0 +1,23 @@ +import type {Docs} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {featureFlag} from 'sentry/gettingStartedDocs/javascript/javascript/featureFlags'; +import {logs} from 'sentry/gettingStartedDocs/javascript/react/logs'; + +import {crashReport} from './crashReport'; +import {feedback} from './feedback'; +import {onboarding} from './onboarding'; +import {performance} from './performance'; +import {profiling} from './profiling'; +import {replay} from './replay'; + +const docs: Docs = { + onboarding, + feedbackOnboardingNpm: feedback, + replayOnboarding: replay, + performanceOnboarding: performance, + crashReportOnboarding: crashReport, + profilingOnboarding: profiling, + logsOnboarding: logs, + featureFlagOnboarding: featureFlag, +}; + +export default docs; diff --git a/static/app/gettingStartedDocs/javascript/react/logs.tsx b/static/app/gettingStartedDocs/javascript/react/logs.tsx new file mode 100644 index 00000000000000..2868ca22548df8 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/logs.tsx @@ -0,0 +1,9 @@ +import {getJavascriptLogsOnboarding} from 'sentry/utils/gettingStartedDocs/javascript'; + +import {installSnippetBlock} from './utils'; + +export const logs = getJavascriptLogsOnboarding({ + installSnippetBlock, + docsPlatform: 'react', + packageName: '@sentry/react', +}); diff --git a/static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx b/static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx new file mode 100644 index 00000000000000..e895f82fe22b2b --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/onboarding.spec.tsx @@ -0,0 +1,121 @@ +import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout'; +import {screen} from 'sentry-test/reactTestingLibrary'; +import {textWithMarkupMatcher} from 'sentry-test/utils'; + +import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types'; + +import docs from '.'; + +describe('javascript-react onboarding docs', () => { + it('renders onboarding docs correctly', () => { + renderWithOnboardingLayout(docs); + + // Renders main headings + expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Configure SDK'})).toBeInTheDocument(); + expect( + screen.getByRole('heading', {name: /Upload Source Maps/i}) + ).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument(); + + // Includes import statement + expect( + screen.getByText(textWithMarkupMatcher(/import \* as Sentry from "@sentry\/react"/)) + ).toBeInTheDocument(); + }); + + it('displays sample rates by default', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/)) + ).toBeInTheDocument(); + }); + + it('enables performance setting the tracesSampleRate to 1', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + }); + + it('enables replay by setting replay samplerates', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/)) + ).toBeInTheDocument(); + }); + + it('enables profiling by setting profiling sample rates', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/Sentry.browserProfilingIntegration\(\)/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + }); + + it('enables logs by setting enableLogs to true', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.LOGS], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/enableLogs: true/)) + ).toBeInTheDocument(); + }); + + it('shows Logging Integrations in next steps when logs is selected', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ProductSolution.LOGS, + ], + }); + + expect(screen.getByText('Logging Integrations')).toBeInTheDocument(); + }); + + it('does not show Logging Integrations in next steps when logs is not selected', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ], + }); + + expect(screen.queryByText('Logging Integrations')).not.toBeInTheDocument(); + }); +}); diff --git a/static/app/gettingStartedDocs/javascript/react/onboarding.tsx b/static/app/gettingStartedDocs/javascript/react/onboarding.tsx new file mode 100644 index 00000000000000..0b11453ad0b163 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/onboarding.tsx @@ -0,0 +1,277 @@ +import {tracePropagationBlock} from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; +import type { + DocsParams, + OnboardingConfig, +} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import { + getAIRulesForCodeEditorStep, + getUploadSourceMapsStep, +} from 'sentry/components/onboarding/gettingStartedDoc/utils'; +import {t, tct} from 'sentry/locale'; + +import {getSdkSetupSnippet, installSnippetBlock} from './utils'; + +const getVerifySnippet = (params: DocsParams) => { + const logsCode = params.isLogsSelected + ? ` + // Send a log before throwing the error + Sentry.logger.info('User triggered test error', { + action: 'test_error_button_click', + });` + : ''; + + return `import * as Sentry from '@sentry/react'; +// Add this button component to your app to test Sentry's error tracking +function ErrorButton() { + return ( + + ); +}`; +}; + +export const onboarding: OnboardingConfig = { + introduction: () => + tct( + "In this quick guide you'll use [strong:npm], [strong:yarn], or [strong:pnpm] to set up:", + { + strong: , + } + ), + install: () => [ + { + type: StepType.INSTALL, + content: [ + { + type: 'text', + text: tct( + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', + {code: } + ), + }, + installSnippetBlock, + ], + }, + ], + configure: (params: DocsParams) => [ + { + type: StepType.CONFIGURE, + content: [ + { + type: 'text', + text: t( + "Initialize Sentry as early as possible in your application's lifecycle." + ), + }, + { + type: 'code', + tabs: [ + { + label: 'JavaScript', + language: 'javascript', + code: getSdkSetupSnippet(params), + }, + ], + }, + { + type: 'conditional', + condition: params.isReplaySelected, + content: [tracePropagationBlock], + }, + ], + }, + getUploadSourceMapsStep({ + guideLink: 'https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/', + ...params, + }), + getAIRulesForCodeEditorStep({ + // ATTENTION: The rules defined here must match those in the documentation (see: https://github.com/getsentry/sentry-docs/blob/master/platform-includes/llm-rules-logs/javascript.react.mdx). + // If you make any changes, please update the docs accordingly. + rules: ` +These examples should be used as guidance when configuring Sentry functionality within a project. + +# Error / Exception Tracking + +Use \`Sentry.captureException(error)\` to capture an exception and log the error in Sentry. +Use this in try catch blocks or areas where exceptions are expected + +# Tracing Examples + +Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls +Ensure you are creating custom spans with meaningful names and operations +Use the \`Sentry.startSpan\` function to create a span +Child spans can exist within a parent span + +## Custom Span instrumentation in component actions + +\`\`\`javascript +function TestComponent() { + const handleTestButtonClick = () => { + // Create a transaction/span to measure performance + Sentry.startSpan( + { + op: "ui.click", + name: "Test Button Click", + }, + (span) => { + const value = "some config"; + const metric = "some metric"; + + // Metrics can be added to the span + span.setAttribute("config", value); + span.setAttribute("metric", metric); + + doSomething(); + }, + ); + }; + + return ( + + ); +} +\`\`\` + +## Custom span instrumentation in API calls + +\`\`\`javascript +async function fetchUserData(userId) { + return Sentry.startSpan( + { + op: "http.client", + name: \`GET /api/users/\${userId}\`, + }, + async () => { + const response = await fetch(\`/api/users/\${userId}\`); + const data = await response.json(); + return data; + }, + ); +} +\`\`\` + +# Logs + +Where logs are used, ensure Sentry is imported using \`import * as Sentry from "@sentry/react"\` +Enable logging in Sentry using \`Sentry.init({ enableLogs: true })\` +Reference the logger using \`const { logger } = Sentry\` +Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls + +## Configuration + +### Baseline + +\`\`\`javascript +import * as Sentry from "@sentry/react"; + +Sentry.init({ + dsn: "${params.dsn.public}", + + enableLogs: true, +}); +\`\`\` + +### Logger Integration + +\`\`\`javascript +Sentry.init({ + dsn: "${params.dsn.public}", + integrations: [ + // send console.log, console.warn, and console.error calls as logs to Sentry + Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }), + ], +}); +\`\`\` + +## Logger Examples + +\`logger.fmt\` is a template literal function that should be used to bring variables into the structured logs. + +\`\`\`javascript +logger.trace("Starting database connection", { database: "users" }); +logger.debug(logger.fmt\`Cache miss for user: \${userId}\`); +logger.info("Updated profile", { profileId: 345 }); +logger.warn("Rate limit reached for endpoint", { + endpoint: "/api/results/", + isEnterprise: false, +}); +logger.error("Failed to process payment", { + orderId: "order_123", + amount: 99.99, +}); +logger.fatal("Database connection pool exhausted", { + database: "users", + activeConnections: 100, +}); +\`\`\` +`, + }), + ], + verify: (params: DocsParams) => [ + { + type: StepType.VERIFY, + content: [ + { + type: 'text', + text: t( + "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." + ), + }, + { + type: 'code', + tabs: [ + { + label: 'React', + language: 'javascript', + code: getVerifySnippet(params), + }, + ], + }, + ], + }, + ], + nextSteps: (params: DocsParams) => { + const steps = [ + { + id: 'react-features', + name: t('React Features'), + description: t( + 'Learn about our first class integration with the React framework.' + ), + link: 'https://docs.sentry.io/platforms/javascript/guides/react/features/', + }, + ]; + + if (params.isPerformanceSelected) { + steps.push({ + id: 'react-router', + name: t('React Router'), + description: t( + 'Configure routing, so Sentry can generate parameterized route names for better grouping of tracing data.' + ), + link: 'https://docs.sentry.io/platforms/javascript/guides/react/configuration/integrations/react-router/', + }); + } + + if (params.isLogsSelected) { + steps.push({ + id: 'logs', + name: t('Logging Integrations'), + description: t( + 'Add logging integrations to automatically capture logs from your application.' + ), + link: 'https://docs.sentry.io/platforms/javascript/guides/react/logs/#integrations', + }); + } + + return steps; + }, +}; diff --git a/static/app/gettingStartedDocs/javascript/react/performance.tsx b/static/app/gettingStartedDocs/javascript/react/performance.tsx new file mode 100644 index 00000000000000..90a85f76b6bf79 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/performance.tsx @@ -0,0 +1,119 @@ +import {ExternalLink} from 'sentry/components/core/link'; +import type {OnboardingConfig} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {t, tct} from 'sentry/locale'; + +import {onboarding} from './onboarding'; + +export const performance: OnboardingConfig = { + introduction: () => + t( + "Adding Performance to your React project is simple. Make sure you've got these basics down." + ), + install: onboarding.install, + configure: params => [ + { + type: StepType.CONFIGURE, + content: [ + { + type: 'text', + text: t( + "Configuration should happen as early as possible in your application's lifecycle." + ), + }, + { + type: 'code', + language: 'javascript', + code: ` +import React from "react"; +import ReactDOM from "react-dom"; +import * as Sentry from "@sentry/react"; +import App from "./App"; + +Sentry.init({ + dsn: "${params.dsn.public}", + integrations: [Sentry.browserTracingIntegration()], + + // Set tracesSampleRate to 1.0 to capture 100% + // of transactions for performance monitoring. + // We recommend adjusting this value in production + tracesSampleRate: 1.0, + // Set \`tracePropagationTargets\` to control for which URLs distributed tracing should be enabled + tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/], +}); + +ReactDOM.render(, document.getElementById("root")); + +// Can also use with React Concurrent Mode +// ReactDOM.createRoot(document.getElementById('root')).render(); +`, + }, + { + type: 'text', + text: tct( + 'We recommend adjusting the value of [code:tracesSampleRate] in production. Learn more about tracing [linkTracingOptions:options], how to use the [linkTracesSampler:traces_sampler] function, or how to do [linkSampleTransactions:sampling].', + { + code: , + linkTracingOptions: ( + + ), + linkTracesSampler: ( + + ), + linkSampleTransactions: ( + + ), + } + ), + }, + ], + }, + { + title: t('Add Distributed Tracing (Optional)'), + content: [ + { + type: 'text', + text: tct( + "If you're using the current version of our JavaScript SDK and have enabled the [code: BrowserTracing] integration, distributed tracing will work out of the box. To get around possible [link:Browser CORS] issues, define your [code:tracePropagationTargets].", + { + code: , + link: ( + + ), + } + ), + }, + { + type: 'code', + language: 'javascript', + code: ` +Sentry.init({ + dsn: "${params.dsn.public}", + integrations: [Sentry.browserTracingIntegration()], + tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/] +}); +`, + }, + ], + }, + ], + verify: () => [ + { + type: StepType.VERIFY, + content: [ + { + type: 'text', + text: tct( + 'Verify that performance monitoring is working correctly with our [link:automatic instrumentation] by simply using your React application.', + { + link: ( + + ), + } + ), + }, + ], + }, + ], + nextSteps: () => [], +}; diff --git a/static/app/gettingStartedDocs/javascript/react/profiling.tsx b/static/app/gettingStartedDocs/javascript/react/profiling.tsx new file mode 100644 index 00000000000000..eecd58bdf6da9c --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/profiling.tsx @@ -0,0 +1,9 @@ +import {getJavascriptProfilingOnboarding} from 'sentry/utils/gettingStartedDocs/javascript'; + +import {installSnippetBlock} from './utils'; + +export const profiling = getJavascriptProfilingOnboarding({ + installSnippetBlock, + docsLink: + 'https://docs.sentry.io/platforms/javascript/guides/react/profiling/browser-profiling/', +}); diff --git a/static/app/gettingStartedDocs/javascript/react/replay.tsx b/static/app/gettingStartedDocs/javascript/react/replay.tsx new file mode 100644 index 00000000000000..f731a048a7a924 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/replay.tsx @@ -0,0 +1,52 @@ +import type {OnboardingConfig} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import { + getReplayConfigureDescription, + getReplayVerifyStep, +} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding'; +import {tct} from 'sentry/locale'; + +import {getSdkSetupSnippet, installSnippetBlock} from './utils'; + +export const replay: OnboardingConfig = { + install: () => [ + { + type: StepType.INSTALL, + content: [ + { + type: 'text', + text: tct( + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]. You need a minimum version 7.27.0 of [code:@sentry/react] in order to use Session Replay. You do not need to install any additional packages.', + {code: } + ), + }, + installSnippetBlock, + ], + }, + ], + configure: params => [ + { + type: StepType.CONFIGURE, + content: [ + { + type: 'text', + text: getReplayConfigureDescription({ + link: 'https://docs.sentry.io/platforms/javascript/guides/react/session-replay/', + }), + }, + { + type: 'code', + tabs: [ + { + label: 'JavaScript', + language: 'javascript', + code: getSdkSetupSnippet(params), + }, + ], + }, + ], + }, + ], + verify: getReplayVerifyStep(), + nextSteps: () => [], +}; diff --git a/static/app/gettingStartedDocs/javascript/react/utils.tsx b/static/app/gettingStartedDocs/javascript/react/utils.tsx new file mode 100644 index 00000000000000..0376bdb418d9c7 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react/utils.tsx @@ -0,0 +1,119 @@ +import {buildSdkConfig} from 'sentry/components/onboarding/gettingStartedDoc/buildSdkConfig'; +import type { + ContentBlock, + DocsParams, +} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {getFeedbackConfigOptions} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; +import {getReplayConfigOptions} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding'; + + +const getDynamicParts = (params: DocsParams): string[] => { + const dynamicParts: string[] = []; + + if (params.isPerformanceSelected) { + dynamicParts.push(` + // Tracing + tracesSampleRate: 1.0, // Capture 100% of the transactions + // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled + tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]`); + } + + if (params.isReplaySelected) { + dynamicParts.push(` + // 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.`); + } + + if (params.isLogsSelected) { + dynamicParts.push(` + // Enable logs to be sent to Sentry + enableLogs: true`); + } + + if (params.isProfilingSelected) { + dynamicParts.push(` + // Set profilesSampleRate to 1.0 to profile every transaction. + // Since profilesSampleRate is relative to tracesSampleRate, + // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate + // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would + // results in 25% of transactions being profiled (0.5*0.5=0.25) + profilesSampleRate: 1.0`); + } + + return dynamicParts; +}; + +const getIntegrations = (params: DocsParams): string[] => { + const integrations = []; + if (params.isPerformanceSelected) { + integrations.push(`Sentry.browserTracingIntegration()`); + } + + if (params.isProfilingSelected) { + integrations.push(`Sentry.browserProfilingIntegration()`); + } + + if (params.isReplaySelected) { + integrations.push( + `Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)})` + ); + } + + if (params.isFeedbackSelected) { + integrations.push(` + Sentry.feedbackIntegration({ + colorScheme: "system", + ${getFeedbackConfigOptions(params.feedbackOptions)} + }),`); + } + + return integrations; +}; + +export function getSdkSetupSnippet(params: DocsParams) { + const config = buildSdkConfig({ + params, + staticParts: [ + `dsn: "${params.dsn.public}"`, + `// Setting this option to true will send default PII data to Sentry. + // For example, automatic IP address collection on events + sendDefaultPii: true`, + ], + getIntegrations, + getDynamicParts, + }); + + return ` +import * as Sentry from "@sentry/react"; + +Sentry.init({ + ${config} +}); + +const container = document.getElementById("app"); +const root = createRoot(container); +root.render(); +`; +} + +export const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/react', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/react', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm add @sentry/react', + }, + ], +}; From 966376f8ec6b9e0e20021af479fca6cef6e824a1 Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:36:37 +0000 Subject: [PATCH 2/4] :hammer_and_wrench: apply pre-commit fixes --- static/app/gettingStartedDocs/javascript/react/utils.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/app/gettingStartedDocs/javascript/react/utils.tsx b/static/app/gettingStartedDocs/javascript/react/utils.tsx index 0376bdb418d9c7..e2d82277581259 100644 --- a/static/app/gettingStartedDocs/javascript/react/utils.tsx +++ b/static/app/gettingStartedDocs/javascript/react/utils.tsx @@ -6,7 +6,6 @@ import type { import {getFeedbackConfigOptions} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; import {getReplayConfigOptions} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding'; - const getDynamicParts = (params: DocsParams): string[] => { const dynamicParts: string[] = []; From d9f3f32c08fdcd225cc3658859511bc0c5f63371 Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Thu, 6 Nov 2025 08:20:40 +0100 Subject: [PATCH 3/4] fix import --- static/app/gettingStartedDocs/javascript/react/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/app/gettingStartedDocs/javascript/react/index.tsx b/static/app/gettingStartedDocs/javascript/react/index.tsx index b8d2f35b78df91..72d5254b184a90 100644 --- a/static/app/gettingStartedDocs/javascript/react/index.tsx +++ b/static/app/gettingStartedDocs/javascript/react/index.tsx @@ -1,5 +1,5 @@ import type {Docs} from 'sentry/components/onboarding/gettingStartedDoc/types'; -import {featureFlag} from 'sentry/gettingStartedDocs/javascript/javascript/featureFlags'; +import {featureFlag} from 'sentry/gettingStartedDocs/javascript/javascript/featureFlag'; import {logs} from 'sentry/gettingStartedDocs/javascript/react/logs'; import {crashReport} from './crashReport'; From 8920c93fcd1196c842da9036aa0e56782913fd32 Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Thu, 6 Nov 2025 08:21:13 +0100 Subject: [PATCH 4/4] delete old files --- .../javascript/react.spec.tsx | 121 ---- .../gettingStartedDocs/javascript/react.tsx | 669 ------------------ 2 files changed, 790 deletions(-) delete mode 100644 static/app/gettingStartedDocs/javascript/react.spec.tsx delete mode 100644 static/app/gettingStartedDocs/javascript/react.tsx diff --git a/static/app/gettingStartedDocs/javascript/react.spec.tsx b/static/app/gettingStartedDocs/javascript/react.spec.tsx deleted file mode 100644 index 3b1febf24bfad6..00000000000000 --- a/static/app/gettingStartedDocs/javascript/react.spec.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout'; -import {screen} from 'sentry-test/reactTestingLibrary'; -import {textWithMarkupMatcher} from 'sentry-test/utils'; - -import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types'; - -import docs from './react'; - -describe('javascript-react onboarding docs', () => { - it('renders onboarding docs correctly', () => { - renderWithOnboardingLayout(docs); - - // Renders main headings - expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument(); - expect(screen.getByRole('heading', {name: 'Configure SDK'})).toBeInTheDocument(); - expect( - screen.getByRole('heading', {name: /Upload Source Maps/i}) - ).toBeInTheDocument(); - expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument(); - - // Includes import statement - expect( - screen.getByText(textWithMarkupMatcher(/import \* as Sentry from "@sentry\/react"/)) - ).toBeInTheDocument(); - }); - - it('displays sample rates by default', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ - ProductSolution.ERROR_MONITORING, - ProductSolution.PERFORMANCE_MONITORING, - ProductSolution.SESSION_REPLAY, - ], - }); - - expect( - screen.getByText(textWithMarkupMatcher(/tracesSampleRate/)) - ).toBeInTheDocument(); - expect( - screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate/)) - ).toBeInTheDocument(); - expect( - screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/)) - ).toBeInTheDocument(); - }); - - it('enables performance setting the tracesSampleRate to 1', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ - ProductSolution.ERROR_MONITORING, - ProductSolution.PERFORMANCE_MONITORING, - ], - }); - - expect( - screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/)) - ).toBeInTheDocument(); - }); - - it('enables replay by setting replay samplerates', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ - ProductSolution.ERROR_MONITORING, - ProductSolution.SESSION_REPLAY, - ], - }); - - expect( - screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/)) - ).toBeInTheDocument(); - expect( - screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/)) - ).toBeInTheDocument(); - }); - - it('enables profiling by setting profiling sample rates', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING], - }); - - expect( - screen.getByText(textWithMarkupMatcher(/Sentry.browserProfilingIntegration\(\)/)) - ).toBeInTheDocument(); - expect( - screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/)) - ).toBeInTheDocument(); - }); - - it('enables logs by setting enableLogs to true', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.LOGS], - }); - - expect( - screen.getByText(textWithMarkupMatcher(/enableLogs: true/)) - ).toBeInTheDocument(); - }); - - it('shows Logging Integrations in next steps when logs is selected', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ - ProductSolution.ERROR_MONITORING, - ProductSolution.PERFORMANCE_MONITORING, - ProductSolution.LOGS, - ], - }); - - expect(screen.getByText('Logging Integrations')).toBeInTheDocument(); - }); - - it('does not show Logging Integrations in next steps when logs is not selected', () => { - renderWithOnboardingLayout(docs, { - selectedProducts: [ - ProductSolution.ERROR_MONITORING, - ProductSolution.PERFORMANCE_MONITORING, - ], - }); - - expect(screen.queryByText('Logging Integrations')).not.toBeInTheDocument(); - }); -}); diff --git a/static/app/gettingStartedDocs/javascript/react.tsx b/static/app/gettingStartedDocs/javascript/react.tsx deleted file mode 100644 index a77e1a7803471d..00000000000000 --- a/static/app/gettingStartedDocs/javascript/react.tsx +++ /dev/null @@ -1,669 +0,0 @@ -import {ExternalLink} from 'sentry/components/core/link'; -import {buildSdkConfig} from 'sentry/components/onboarding/gettingStartedDoc/buildSdkConfig'; -import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout'; -import {widgetCalloutBlock} from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; -import {tracePropagationBlock} from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; -import type { - ContentBlock, - Docs, - DocsParams, - OnboardingConfig, -} from 'sentry/components/onboarding/gettingStartedDoc/types'; -import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; -import { - getAIRulesForCodeEditorStep, - getUploadSourceMapsStep, -} from 'sentry/components/onboarding/gettingStartedDoc/utils'; -import { - getCrashReportJavaScriptInstallSteps, - getCrashReportModalConfigDescription, - getCrashReportModalIntroduction, - getFeedbackConfigOptions, - getFeedbackConfigureDescription, -} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; -import { - getReplayConfigOptions, - getReplayConfigureDescription, - getReplayVerifyStep, -} from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding'; -import {featureFlag} from 'sentry/gettingStartedDocs/javascript/javascript/featureFlag'; -import {t, tct} from 'sentry/locale'; -import { - getJavascriptLogsOnboarding, - getJavascriptProfilingOnboarding, -} from 'sentry/utils/gettingStartedDocs/javascript'; - -type Params = DocsParams; - -const getDynamicParts = (params: Params): string[] => { - const dynamicParts: string[] = []; - - if (params.isPerformanceSelected) { - dynamicParts.push(` - // Tracing - tracesSampleRate: 1.0, // Capture 100% of the transactions - // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/]`); - } - - if (params.isReplaySelected) { - dynamicParts.push(` - // 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.`); - } - - if (params.isLogsSelected) { - dynamicParts.push(` - // Enable logs to be sent to Sentry - enableLogs: true`); - } - - if (params.isProfilingSelected) { - dynamicParts.push(` - // Set profilesSampleRate to 1.0 to profile every transaction. - // Since profilesSampleRate is relative to tracesSampleRate, - // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate - // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would - // results in 25% of transactions being profiled (0.5*0.5=0.25) - profilesSampleRate: 1.0`); - } - - return dynamicParts; -}; - -const getIntegrations = (params: Params): string[] => { - const integrations = []; - if (params.isPerformanceSelected) { - integrations.push(`Sentry.browserTracingIntegration()`); - } - - if (params.isProfilingSelected) { - integrations.push(`Sentry.browserProfilingIntegration()`); - } - - if (params.isReplaySelected) { - integrations.push( - `Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)})` - ); - } - - if (params.isFeedbackSelected) { - integrations.push(` - Sentry.feedbackIntegration({ - colorScheme: "system", - ${getFeedbackConfigOptions(params.feedbackOptions)} - }),`); - } - - return integrations; -}; - -const getSdkSetupSnippet = (params: Params) => { - const config = buildSdkConfig({ - params, - staticParts: [ - `dsn: "${params.dsn.public}"`, - `// Setting this option to true will send default PII data to Sentry. - // For example, automatic IP address collection on events - sendDefaultPii: true`, - ], - getIntegrations, - getDynamicParts, - }); - - return ` -import * as Sentry from "@sentry/react"; - -Sentry.init({ - ${config} -}); - -const container = document.getElementById(“app”); -const root = createRoot(container); -root.render(); -`; -}; - -const getVerifySnippet = (params: Params) => { - const logsCode = params.isLogsSelected - ? ` - // Send a log before throwing the error - Sentry.logger.info('User triggered test error', { - action: 'test_error_button_click', - });` - : ''; - - return `import * as Sentry from '@sentry/react'; -// Add this button component to your app to test Sentry's error tracking -function ErrorButton() { - return ( - - ); -}`; -}; - -const installSnippetBlock: ContentBlock = { - type: 'code', - tabs: [ - { - label: 'npm', - language: 'bash', - code: 'npm install --save @sentry/react', - }, - { - label: 'yarn', - language: 'bash', - code: 'yarn add @sentry/react', - }, - { - label: 'pnpm', - language: 'bash', - code: 'pnpm add @sentry/react', - }, - ], -}; - -const onboarding: OnboardingConfig = { - introduction: () => - tct( - "In this quick guide you'll use [strong:npm], [strong:yarn], or [strong:pnpm] to set up:", - { - strong: , - } - ), - install: () => [ - { - type: StepType.INSTALL, - content: [ - { - type: 'text', - text: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', - {code: } - ), - }, - installSnippetBlock, - ], - }, - ], - configure: (params: Params) => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: t( - "Initialize Sentry as early as possible in your application's lifecycle." - ), - }, - { - type: 'code', - tabs: [ - { - label: 'JavaScript', - language: 'javascript', - code: getSdkSetupSnippet(params), - }, - ], - }, - { - type: 'conditional', - condition: params.isReplaySelected, - content: [tracePropagationBlock], - }, - ], - }, - getUploadSourceMapsStep({ - guideLink: 'https://docs.sentry.io/platforms/javascript/guides/react/sourcemaps/', - ...params, - }), - getAIRulesForCodeEditorStep({ - // ATTENTION: The rules defined here must match those in the documentation (see: https://github.com/getsentry/sentry-docs/blob/master/platform-includes/llm-rules-logs/javascript.react.mdx). - // If you make any changes, please update the docs accordingly. - rules: ` -These examples should be used as guidance when configuring Sentry functionality within a project. - -# Error / Exception Tracking - -Use \`Sentry.captureException(error)\` to capture an exception and log the error in Sentry. -Use this in try catch blocks or areas where exceptions are expected - -# Tracing Examples - -Spans should be created for meaningful actions within an applications like button clicks, API calls, and function calls -Ensure you are creating custom spans with meaningful names and operations -Use the \`Sentry.startSpan\` function to create a span -Child spans can exist within a parent span - -## Custom Span instrumentation in component actions - -\`\`\`javascript -function TestComponent() { - const handleTestButtonClick = () => { - // Create a transaction/span to measure performance - Sentry.startSpan( - { - op: "ui.click", - name: "Test Button Click", - }, - (span) => { - const value = "some config"; - const metric = "some metric"; - - // Metrics can be added to the span - span.setAttribute("config", value); - span.setAttribute("metric", metric); - - doSomething(); - }, - ); - }; - - return ( - - ); -} -\`\`\` - -## Custom span instrumentation in API calls - -\`\`\`javascript -async function fetchUserData(userId) { - return Sentry.startSpan( - { - op: "http.client", - name: \`GET /api/users/\${userId}\`, - }, - async () => { - const response = await fetch(\`/api/users/\${userId}\`); - const data = await response.json(); - return data; - }, - ); -} -\`\`\` - -# Logs - -Where logs are used, ensure Sentry is imported using \`import * as Sentry from "@sentry/react"\` -Enable logging in Sentry using \`Sentry.init({ enableLogs: true })\` -Reference the logger using \`const { logger } = Sentry\` -Sentry offers a consoleLoggingIntegration that can be used to log specific console error types automatically without instrumenting the individual logger calls - -## Configuration - -### Baseline - -\`\`\`javascript -import * as Sentry from "@sentry/react"; - -Sentry.init({ - dsn: "${params.dsn.public}", - - enableLogs: true, -}); -\`\`\` - -### Logger Integration - -\`\`\`javascript -Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [ - // send console.log, console.warn, and console.error calls as logs to Sentry - Sentry.consoleLoggingIntegration({ levels: ["log", "warn", "error"] }), - ], -}); -\`\`\` - -## Logger Examples - -\`logger.fmt\` is a template literal function that should be used to bring variables into the structured logs. - -\`\`\`javascript -logger.trace("Starting database connection", { database: "users" }); -logger.debug(logger.fmt\`Cache miss for user: \${userId}\`); -logger.info("Updated profile", { profileId: 345 }); -logger.warn("Rate limit reached for endpoint", { - endpoint: "/api/results/", - isEnterprise: false, -}); -logger.error("Failed to process payment", { - orderId: "order_123", - amount: 99.99, -}); -logger.fatal("Database connection pool exhausted", { - database: "users", - activeConnections: 100, -}); -\`\`\` -`, - }), - ], - verify: (params: Params) => [ - { - type: StepType.VERIFY, - content: [ - { - type: 'text', - text: t( - "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." - ), - }, - { - type: 'code', - tabs: [ - { - label: 'React', - language: 'javascript', - code: getVerifySnippet(params), - }, - ], - }, - ], - }, - ], - nextSteps: (params: Params) => { - const steps = [ - { - id: 'react-features', - name: t('React Features'), - description: t( - 'Learn about our first class integration with the React framework.' - ), - link: 'https://docs.sentry.io/platforms/javascript/guides/react/features/', - }, - ]; - - if (params.isPerformanceSelected) { - steps.push({ - id: 'react-router', - name: t('React Router'), - description: t( - 'Configure routing, so Sentry can generate parameterized route names for better grouping of tracing data.' - ), - link: 'https://docs.sentry.io/platforms/javascript/guides/react/configuration/integrations/react-router/', - }); - } - - if (params.isLogsSelected) { - steps.push({ - id: 'logs', - name: t('Logging Integrations'), - description: t( - 'Add logging integrations to automatically capture logs from your application.' - ), - link: 'https://docs.sentry.io/platforms/javascript/guides/react/logs/#integrations', - }); - } - - return steps; - }, -}; - -const replayOnboarding: OnboardingConfig = { - install: () => [ - { - type: StepType.INSTALL, - content: [ - { - type: 'text', - text: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]. You need a minimum version 7.27.0 of [code:@sentry/react] in order to use Session Replay. You do not need to install any additional packages.', - {code: } - ), - }, - installSnippetBlock, - ], - }, - ], - configure: (params: Params) => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: getReplayConfigureDescription({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/session-replay/', - }), - }, - { - type: 'code', - tabs: [ - { - label: 'JavaScript', - language: 'javascript', - code: getSdkSetupSnippet(params), - }, - ], - }, - ], - }, - ], - verify: getReplayVerifyStep(), - nextSteps: () => [], -}; - -const feedbackOnboarding: OnboardingConfig = { - install: () => [ - { - type: StepType.INSTALL, - content: [ - { - type: 'text', - text: tct( - 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/react]) installed, minimum version 7.85.0.', - { - code: , - } - ), - }, - installSnippetBlock, - ], - }, - ], - configure: (params: Params) => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: getFeedbackConfigureDescription({ - linkConfig: - 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/', - linkButton: - 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#bring-your-own-button', - }), - }, - { - type: 'code', - tabs: [ - { - label: 'JavaScript', - language: 'javascript', - code: getSdkSetupSnippet(params), - }, - ], - }, - { - type: 'custom', - content: crashReportCallout({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#crash-report-modal', - }), - }, - ], - }, - ], - verify: () => [], - nextSteps: () => [], -}; - -const crashReportOnboarding: OnboardingConfig = { - introduction: () => getCrashReportModalIntroduction(), - install: (params: Params) => getCrashReportJavaScriptInstallSteps(params), - configure: () => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: getCrashReportModalConfigDescription({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/configuration/#crash-report-modal', - }), - }, - widgetCalloutBlock({ - link: 'https://docs.sentry.io/platforms/javascript/guides/react/user-feedback/#user-feedback-widget', - }), - ], - }, - ], - verify: () => [], - nextSteps: () => [], -}; - -const performanceOnboarding: OnboardingConfig = { - introduction: () => - t( - "Adding Performance to your React project is simple. Make sure you've got these basics down." - ), - install: onboarding.install, - configure: params => [ - { - type: StepType.CONFIGURE, - content: [ - { - type: 'text', - text: t( - "Configuration should happen as early as possible in your application's lifecycle." - ), - }, - { - type: 'code', - language: 'javascript', - code: ` -import React from "react"; -import ReactDOM from "react-dom"; -import * as Sentry from "@sentry/react"; -import App from "./App"; - -Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [Sentry.browserTracingIntegration()], - - // Set tracesSampleRate to 1.0 to capture 100% - // of transactions for performance monitoring. - // We recommend adjusting this value in production - tracesSampleRate: 1.0, - // Set \`tracePropagationTargets\` to control for which URLs distributed tracing should be enabled - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/], -}); - -ReactDOM.render(, document.getElementById("root")); - -// Can also use with React Concurrent Mode -// ReactDOM.createRoot(document.getElementById('root')).render(); -`, - }, - { - type: 'text', - text: tct( - 'We recommend adjusting the value of [code:tracesSampleRate] in production. Learn more about tracing [linkTracingOptions:options], how to use the [linkTracesSampler:traces_sampler] function, or how to do [linkSampleTransactions:sampling].', - { - code: , - linkTracingOptions: ( - - ), - linkTracesSampler: ( - - ), - linkSampleTransactions: ( - - ), - } - ), - }, - ], - }, - { - title: t('Add Distributed Tracing (Optional)'), - content: [ - { - type: 'text', - text: tct( - "If you're using the current version of our JavaScript SDK and have enabled the [code: BrowserTracing] integration, distributed tracing will work out of the box. To get around possible [link:Browser CORS] issues, define your [code:tracePropagationTargets].", - { - code: , - link: ( - - ), - } - ), - }, - { - type: 'code', - language: 'javascript', - code: ` -Sentry.init({ - dsn: "${params.dsn.public}", - integrations: [Sentry.browserTracingIntegration()], - tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/] -}); -`, - }, - ], - }, - ], - verify: () => [ - { - type: StepType.VERIFY, - content: [ - { - type: 'text', - text: tct( - 'Verify that performance monitoring is working correctly with our [link:automatic instrumentation] by simply using your React application.', - { - link: ( - - ), - } - ), - }, - ], - }, - ], - nextSteps: () => [], -}; - -const profilingOnboarding = getJavascriptProfilingOnboarding({ - installSnippetBlock, - docsLink: - 'https://docs.sentry.io/platforms/javascript/guides/react/profiling/browser-profiling/', -}); - -const logsOnboarding: OnboardingConfig = getJavascriptLogsOnboarding({ - installSnippetBlock, - docsPlatform: 'react', - packageName: '@sentry/react', -}); - -const docs: Docs = { - onboarding, - feedbackOnboardingNpm: feedbackOnboarding, - replayOnboarding, - performanceOnboarding, - crashReportOnboarding, - profilingOnboarding, - logsOnboarding, - featureFlagOnboarding: featureFlag, -}; - -export default docs;