react-native.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. import {Fragment} from 'react';
  2. import ExternalLink from 'sentry/components/links/externalLink';
  3. import List from 'sentry/components/list/';
  4. import ListItem from 'sentry/components/list/listItem';
  5. import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  6. import type {
  7. Docs,
  8. DocsParams,
  9. OnboardingConfig,
  10. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  11. import {MobileBetaBanner} from 'sentry/components/onboarding/gettingStartedDoc/utils';
  12. import {
  13. getCrashReportApiIntroduction,
  14. getCrashReportInstallDescription,
  15. } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
  16. import {getReactNativeMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
  17. import {
  18. getReplayMobileConfigureDescription,
  19. getReplayVerifyStep,
  20. } from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
  21. import {t, tct} from 'sentry/locale';
  22. type Params = DocsParams;
  23. const getConfigureSnippet = (params: Params) => `
  24. import * as Sentry from "@sentry/react-native";
  25. Sentry.init({
  26. dsn: "${params.dsn.public}",${
  27. params.isPerformanceSelected
  28. ? `
  29. // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
  30. // We recommend adjusting this value in production.
  31. tracesSampleRate: 1.0,`
  32. : ''
  33. }${
  34. params.isProfilingSelected
  35. ? `
  36. // profilesSampleRate is relative to tracesSampleRate.
  37. // Here, we'll capture profiles for 100% of transactions.
  38. profilesSampleRate: 1.0,`
  39. : ''
  40. }
  41. });`;
  42. const getPerformanceSnippet = () => `
  43. // Let's say this function is invoked when a user clicks on the checkout button of your shop
  44. shopCheckout() {
  45. // This will create a new Transaction for you
  46. const transaction = Sentry.startTransaction({ name: "shopCheckout" });
  47. // Set transaction on scope to associate with errors and get included span instrumentation
  48. // If there's currently an unfinished transaction, it may be dropped
  49. Sentry.getCurrentHub().configureScope(scope => scope.setSpan(transaction));
  50. // Assume this function makes an xhr/fetch call
  51. const result = validateShoppingCartOnServer();
  52. const span = transaction.startChild({
  53. data: {
  54. result
  55. },
  56. op: 'task',
  57. description: "processing shopping cart result",
  58. });
  59. try {
  60. processAndValidateShoppingCart(result);
  61. span.setStatus(SpanStatus.Ok);
  62. } catch (err) {
  63. span.setStatus(SpanStatus.UnknownError);
  64. throw err;
  65. } finally {
  66. span.finish();
  67. transaction.finish();
  68. }
  69. }`;
  70. const getReplaySetupSnippet = (params: Params) => `
  71. import * as Sentry from '@sentry/react-native';
  72. Sentry.init({
  73. dsn: "${params.dsn.public}",
  74. _experiments: {
  75. replaysSessionSampleRate: 1.0,
  76. replaysOnErrorSampleRate: 1.0,
  77. },
  78. integrations: [
  79. Sentry.mobileReplayIntegration(),
  80. ],
  81. });`;
  82. const getReplayConfigurationSnippet = () => `
  83. Sentry.mobileReplayIntegration({
  84. maskAllText: true,
  85. maskAllImages: true,
  86. maskAllVectors: true,
  87. }),`;
  88. const onboarding: OnboardingConfig = {
  89. install: () => [
  90. {
  91. type: StepType.INSTALL,
  92. description: tct(
  93. 'Sentry captures data by using an SDK within your application’s runtime. If you are using Expo, see [expoLink:How to Add Sentry to Your Expo Project]. This SDK works for both managed and bare projects.',
  94. {expoLink: <ExternalLink href="https://docs.expo.dev/guides/using-sentry/" />}
  95. ),
  96. configurations: [
  97. {
  98. language: 'bash',
  99. description: <div>{tct('Run [code:@sentry/wizard]:', {code: <code />})}</div>,
  100. code: 'npx @sentry/wizard@latest -s -i reactNative',
  101. additionalInfo: (
  102. <Fragment>
  103. <p>
  104. {tct(
  105. '[wizardLink:Sentry Wizard] will patch your project accordingly, though you can [setupManuallyLink:setup manually] if you prefer.',
  106. {
  107. wizardLink: (
  108. <ExternalLink href="https://github.com/getsentry/sentry-wizard" />
  109. ),
  110. setupManuallyLink: (
  111. <ExternalLink href="https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/" />
  112. ),
  113. }
  114. )}
  115. </p>
  116. <List symbol="bullet">
  117. <ListItem>
  118. {t(
  119. 'iOS Specifics: When you use Xcode, you can hook directly into the build process to upload debug symbols and source maps.'
  120. )}
  121. </ListItem>
  122. <ListItem>
  123. {tct(
  124. "Android Specifics: We hook into Gradle for the source map build process. When you run [code:./gradlew] assembleRelease, source maps are automatically built and uploaded to Sentry. If you have enabled Gradle's [code:org.gradle.configureondemand] feature, you'll need a clean build, or you'll need to disable this feature to upload the source map on every build by setting [code:org.gradle.configureondemand=false] or remove it.",
  125. {
  126. code: <code />,
  127. }
  128. )}
  129. </ListItem>
  130. </List>
  131. </Fragment>
  132. ),
  133. },
  134. ],
  135. },
  136. ],
  137. configure: params => [
  138. {
  139. type: StepType.CONFIGURE,
  140. configurations: [
  141. ...(params.isProfilingSelected
  142. ? [
  143. {
  144. description: t(
  145. 'React Native Profiling is available since SDK version 5.32.0.'
  146. ),
  147. },
  148. ]
  149. : []),
  150. {
  151. language: 'javascript',
  152. code: getConfigureSnippet(params),
  153. additionalInfo: tct(
  154. 'The "sentry-wizard" will try to add it to your [code:App.tsx]',
  155. {
  156. code: <code />,
  157. }
  158. ),
  159. },
  160. {
  161. language: 'javascript',
  162. description: tct(
  163. 'Wrap your app with Sentry to automatically instrument it with [touchEventTrakingLink:touch event tracking] and [automaticPerformanceMonitoringLink:automatic tracing]:',
  164. {
  165. touchEventTrakingLink: (
  166. <ExternalLink href="https://docs.sentry.io/platforms/react-native/touchevents/" />
  167. ),
  168. automaticPerformanceMonitoringLink: (
  169. <ExternalLink href="https://docs.sentry.io/platforms/react-native/tracing/instrumentation/automatic-instrumentation/" />
  170. ),
  171. }
  172. ),
  173. code: 'export default Sentry.wrap(App);',
  174. additionalInfo: t(
  175. 'You do not need to do this for Sentry to work or if your app does not have a single parent "App" component.'
  176. ),
  177. },
  178. ],
  179. },
  180. ],
  181. verify: (params: Params) => [
  182. {
  183. type: StepType.VERIFY,
  184. description: t(
  185. 'Then create an intentional error, so you can test that everything is working:'
  186. ),
  187. configurations: [
  188. {
  189. language: 'javascript',
  190. code: "throw new Error('My first Sentry error!');",
  191. },
  192. {
  193. language: 'javascript',
  194. description: t('Or, try a native crash with:'),
  195. code: 'Sentry.nativeCrash();',
  196. additionalInfo: (
  197. <Fragment>
  198. {t(
  199. "If you're new to Sentry, use the email alert to access your account and complete a product tour."
  200. )}
  201. {t(
  202. "If you're an existing user and have disabled alerts, you won't receive this email."
  203. )}
  204. </Fragment>
  205. ),
  206. },
  207. ],
  208. },
  209. ...(params.isPerformanceSelected
  210. ? [
  211. {
  212. title: t('Tracing'),
  213. description: (
  214. <Fragment>
  215. {t(
  216. 'Sentry can measure the performance of your app automatically when instrumented with the following routers:'
  217. )}
  218. <List symbol="bullet">
  219. <ListItem>
  220. <ExternalLink href="https://docs.sentry.io/platforms/react-native/tracing/instrumentation/react-navigation/">
  221. {t('React Navigation')}
  222. </ExternalLink>
  223. </ListItem>
  224. <ListItem>
  225. <ExternalLink href="https://docs.sentry.io/platforms/react-native/tracing/instrumentation/react-navigation-v4/">
  226. {t('React Navigation V4 and prior')}
  227. </ExternalLink>
  228. </ListItem>
  229. <ListItem>
  230. <ExternalLink href="https://docs.sentry.io/platforms/react-native/tracing/instrumentation/react-native-navigation/">
  231. {t('React Native Navigation')}
  232. </ExternalLink>
  233. </ListItem>
  234. <ListItem>
  235. <ExternalLink href="https://docs.sentry.io/platforms/react-native/tracing/instrumentation/expo-router/">
  236. {t('Expo Router')}
  237. </ExternalLink>
  238. </ListItem>
  239. </List>
  240. {t(
  241. 'Additionally, you can create transactions and spans programatically:'
  242. )}
  243. </Fragment>
  244. ),
  245. configurations: [
  246. {
  247. description: t('For example:'),
  248. language: 'javascript',
  249. code: getPerformanceSnippet(),
  250. additionalInfo: tct(
  251. 'For more information, please refer to the [docLink: Sentry React Native documentation].',
  252. {
  253. docLink: (
  254. <ExternalLink href="https://docs.sentry.io/platforms/react-native/tracing/instrumentation/" />
  255. ),
  256. }
  257. ),
  258. },
  259. ],
  260. },
  261. ]
  262. : []),
  263. {
  264. title: t('Debug Symbols'),
  265. description: (
  266. <Fragment>
  267. {t(
  268. 'We offer a range of methods to provide Sentry with debug symbols so that you can see symbolicated stack traces and triage issues faster.'
  269. )}
  270. <p>
  271. {tct(
  272. "Complete stack traces will be shown for React Native Javascript errors by default using Sentry's [automaticSourceMapsUploadLink:automatic source maps upload]. To set up manual source maps upload follow [guideLink:this guide].",
  273. {
  274. automaticSourceMapsUploadLink: (
  275. <ExternalLink href="https://docs.sentry.io/platforms/react-native/sourcemaps/" />
  276. ),
  277. guideLink: (
  278. <ExternalLink href="https://docs.sentry.io/platforms/react-native/sourcemaps/" />
  279. ),
  280. }
  281. )}
  282. </p>
  283. <p>
  284. {tct(
  285. "You'll also need to upload [debugSymbolsLink:Debug Symbols] generated by the native iOS and Android tooling for native crashes.",
  286. {
  287. debugSymbolsLink: (
  288. <ExternalLink href="https://docs.sentry.io/platforms/react-native/upload-debug/" />
  289. ),
  290. }
  291. )}
  292. </p>
  293. </Fragment>
  294. ),
  295. },
  296. {
  297. title: t('Source Context'),
  298. description: (
  299. <Fragment>
  300. <p>
  301. {tct(
  302. "If Sentry has access to your application's source code, it can show snippets of code [italic:(source context)] around the location of stack frames, which helps to quickly pinpoint problematic code.",
  303. {
  304. italic: <i />,
  305. }
  306. )}
  307. </p>
  308. <p>
  309. {tct(
  310. 'Source Context will be shown for React Native Javascript error by default if source maps are uploaded. To set up source maps upload, follow the [sourceMapsGuideLink:Source Maps guide].',
  311. {
  312. sourceMapsGuideLink: (
  313. <ExternalLink href="https://docs.sentry.io/platforms/react-native/sourcemaps/" />
  314. ),
  315. }
  316. )}
  317. </p>
  318. <p>
  319. {tct(
  320. "To enable source context for native errors, you'll need to upload native debug symbols to Sentry by following the instructions at [uploadWithGradleLink:Uploading Source Code Context With Sentry Gradle Plugin] and Uploading Source Context With Xcode.",
  321. {
  322. uploadWithGradleLink: (
  323. <ExternalLink href="https://docs.sentry.io/platforms/react-native/upload-debug/#uploading-source-context-with-sentry-gradle-plugin" />
  324. ),
  325. uploadWithXCodeLink: (
  326. <ExternalLink href="https://docs.sentry.io/platforms/react-native/upload-debug/#uploading-source-context-with-xcode" />
  327. ),
  328. }
  329. )}
  330. </p>
  331. </Fragment>
  332. ),
  333. },
  334. ],
  335. };
  336. const feedbackOnboardingCrashApi: OnboardingConfig = {
  337. introduction: () => getCrashReportApiIntroduction(),
  338. install: () => [
  339. {
  340. type: StepType.INSTALL,
  341. description: getCrashReportInstallDescription(),
  342. configurations: [
  343. {
  344. code: [
  345. {
  346. label: 'TypeScript',
  347. value: 'typescript',
  348. language: 'typescript',
  349. code: `import * as Sentry from "@sentry/react-native";
  350. import { UserFeedback } from "@sentry/react-native";
  351. const sentryId = Sentry.captureMessage("My Message");
  352. // OR: const sentryId = Sentry.lastEventId();
  353. const userFeedback: UserFeedback = {
  354. event_id: sentryId,
  355. name: "John Doe",
  356. email: "john@doe.com",
  357. comments: "Hello World!",
  358. };
  359. Sentry.captureUserFeedback(userFeedback);`,
  360. },
  361. ],
  362. },
  363. ],
  364. },
  365. ],
  366. configure: () => [],
  367. verify: () => [],
  368. nextSteps: () => [],
  369. };
  370. const getInstallConfig = () => [
  371. {
  372. language: 'bash',
  373. code: [
  374. {
  375. label: 'npm',
  376. value: 'npm',
  377. language: 'bash',
  378. code: 'npm install --save @sentry/react-native',
  379. },
  380. {
  381. label: 'yarn',
  382. value: 'yarn',
  383. language: 'bash',
  384. code: 'yarn add @sentry/react-native',
  385. },
  386. ],
  387. },
  388. ];
  389. const replayOnboarding: OnboardingConfig = {
  390. introduction: () => (
  391. <MobileBetaBanner link="https://docs.sentry.io/platforms/react-native/session-replay/" />
  392. ),
  393. install: (params: Params) => [
  394. {
  395. type: StepType.INSTALL,
  396. description: t(
  397. 'Make sure your Sentry React Native SDK version is at least 5.26.0. If you already have the SDK installed, you can update it to the latest version with:'
  398. ),
  399. configurations: [
  400. {
  401. code: [
  402. {
  403. label: 'npm',
  404. value: 'npm',
  405. language: 'bash',
  406. code: `npm install @sentry/react-native --save`,
  407. },
  408. {
  409. label: 'yarn',
  410. value: 'yarn',
  411. language: 'bash',
  412. code: `yarn add @sentry/react-native`,
  413. },
  414. {
  415. label: 'pnpm',
  416. value: 'pnpm',
  417. language: 'bash',
  418. code: `pnpm add @sentry/react-native`,
  419. },
  420. ],
  421. },
  422. {
  423. description: t(
  424. 'To set up the integration, add the following to your Sentry initialization:'
  425. ),
  426. },
  427. {
  428. code: [
  429. {
  430. label: 'JavaScript',
  431. value: 'javascript',
  432. language: 'javascript',
  433. code: getReplaySetupSnippet(params),
  434. },
  435. ],
  436. },
  437. ],
  438. },
  439. ],
  440. configure: () => [
  441. {
  442. type: StepType.CONFIGURE,
  443. description: getReplayMobileConfigureDescription({
  444. link: 'https://docs.sentry.io/platforms/react-native/session-replay/#privacy',
  445. }),
  446. configurations: [
  447. {
  448. description: t(
  449. 'The following code is the default configuration, which masks and blocks everything.'
  450. ),
  451. code: [
  452. {
  453. label: 'JavaScript',
  454. value: 'javascript',
  455. language: 'javascript',
  456. code: getReplayConfigurationSnippet(),
  457. },
  458. ],
  459. },
  460. ],
  461. },
  462. ],
  463. verify: getReplayVerifyStep(),
  464. nextSteps: () => [],
  465. };
  466. const docs: Docs = {
  467. onboarding,
  468. feedbackOnboardingCrashApi,
  469. crashReportOnboarding: feedbackOnboardingCrashApi,
  470. customMetricsOnboarding: getReactNativeMetricsOnboarding({getInstallConfig}),
  471. replayOnboarding,
  472. };
  473. export default docs;