flutter.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. import {Fragment} from 'react';
  2. import {Alert} from 'sentry/components/core/alert';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import List from 'sentry/components/list';
  5. import ListItem from 'sentry/components/list/listItem';
  6. import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  7. import type {
  8. BasePlatformOptions,
  9. Docs,
  10. DocsParams,
  11. OnboardingConfig,
  12. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  13. import {
  14. getReplayMobileConfigureDescription,
  15. getReplayVerifyStep,
  16. } from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
  17. import {feedbackOnboardingCrashApiDart} from 'sentry/gettingStartedDocs/dart/dart';
  18. import {t, tct} from 'sentry/locale';
  19. import {getPackageVersion} from 'sentry/utils/gettingStartedDocs/getPackageVersion';
  20. export enum InstallationMode {
  21. AUTO = 'auto',
  22. MANUAL = 'manual',
  23. }
  24. const platformOptions = {
  25. installationMode: {
  26. label: t('Installation Mode'),
  27. items: [
  28. {
  29. label: t('Auto'),
  30. value: InstallationMode.AUTO,
  31. },
  32. {
  33. label: t('Manual'),
  34. value: InstallationMode.MANUAL,
  35. },
  36. ],
  37. defaultValue:
  38. navigator.userAgent.indexOf('Win') !== -1
  39. ? InstallationMode.MANUAL
  40. : InstallationMode.AUTO,
  41. },
  42. } satisfies BasePlatformOptions;
  43. type PlatformOptions = typeof platformOptions;
  44. type Params = DocsParams<PlatformOptions>;
  45. const isAutoInstall = (params: Params) =>
  46. params.platformOptions?.installationMode === InstallationMode.AUTO;
  47. const getInstallSnippet = ({isSelfHosted, organization, projectSlug}: Params) => {
  48. const urlParam = isSelfHosted ? '' : '--saas';
  49. return `brew install getsentry/tools/sentry-wizard && sentry-wizard -i flutter ${urlParam} --org ${organization.slug} --project ${projectSlug}`;
  50. };
  51. const getManualInstallSnippet = (params: Params) => {
  52. const version = getPackageVersion(params, 'sentry.dart.flutter', '8.13.2');
  53. return `dependencies:
  54. sentry_flutter: ^${version}`;
  55. };
  56. const getConfigureSnippet = (params: Params) => `
  57. import 'package:sentry_flutter/sentry_flutter.dart';
  58. Future<void> main() async {
  59. await SentryFlutter.init(
  60. (options) {
  61. options.dsn = '${params.dsn.public}';${
  62. params.isPerformanceSelected
  63. ? `
  64. // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
  65. // We recommend adjusting this value in production.
  66. options.tracesSampleRate = 1.0;`
  67. : ''
  68. }${
  69. params.isProfilingSelected
  70. ? `
  71. // The sampling rate for profiling is relative to tracesSampleRate
  72. // Setting to 1.0 will profile 100% of sampled transactions:
  73. options.profilesSampleRate = 1.0;`
  74. : ''
  75. }
  76. },
  77. appRunner: () => runApp(
  78. SentryWidget(
  79. child: MyApp(),
  80. ),
  81. ),
  82. );
  83. // or define SENTRY_DSN via Dart environment variable (--dart-define)
  84. }`;
  85. const configureAdditionalInfo = tct(
  86. 'You can configure the [code: SENTRY_DSN], [code: SENTRY_RELEASE], [code: SENTRY_DIST], and [code: SENTRY_ENVIRONMENT] via the Dart environment variables passing the [code: --dart-define] flag to the compiler, as noted in the code sample.',
  87. {
  88. code: <code />,
  89. }
  90. );
  91. const getVerifySnippet = () => `
  92. child: ElevatedButton(
  93. onPressed: () {
  94. throw Exception('This is test exception');
  95. },
  96. child: const Text('Verify Sentry Setup'),
  97. )
  98. `;
  99. const getPerformanceSnippet = () => `
  100. import 'package:sentry/sentry.dart';
  101. void execute() async {
  102. final transaction = Sentry.startTransaction('processOrderBatch()', 'task');
  103. try {
  104. await processOrderBatch(transaction);
  105. } catch (exception) {
  106. transaction.throwable = exception;
  107. transaction.status = const SpanStatus.internalError();
  108. } finally {
  109. await transaction.finish();
  110. }
  111. }
  112. Future<void> processOrderBatch(ISentrySpan span) async {
  113. // span operation: task, span description: operation
  114. final innerSpan = span.startChild('task', description: 'operation');
  115. try {
  116. // omitted code
  117. } catch (exception) {
  118. innerSpan.throwable = exception;
  119. innerSpan.status = const SpanStatus.notFound();
  120. } finally {
  121. await innerSpan.finish();
  122. }
  123. }`;
  124. const getInstallReplaySnippet = () => `
  125. await SentryFlutter.init(
  126. (options) {
  127. ...
  128. options.experimental.replay.sessionSampleRate = 1.0;
  129. options.experimental.replay.onErrorSampleRate = 1.0;
  130. },
  131. appRunner: () => runApp(
  132. SentryWidget(
  133. child: MyApp(),
  134. ),
  135. ),
  136. );
  137. `;
  138. const getConfigureReplaySnippet = () => `
  139. options.experimental.replay.maskAllText = true;
  140. options.experimental.replay.maskAllImages = true;`;
  141. const onboarding: OnboardingConfig<PlatformOptions> = {
  142. install: params =>
  143. isAutoInstall(params)
  144. ? [
  145. {
  146. type: StepType.INSTALL,
  147. description: tct(
  148. 'Add Sentry automatically to your app with the [wizardLink:Sentry wizard] (call this inside your project directory).',
  149. {
  150. wizardLink: (
  151. <ExternalLink href="https://docs.sentry.io/platforms/flutter/#install" />
  152. ),
  153. }
  154. ),
  155. configurations: [
  156. {
  157. language: 'bash',
  158. code: getInstallSnippet(params),
  159. },
  160. {
  161. description: (
  162. <Fragment>
  163. <p>
  164. {t(
  165. 'The Sentry wizard will automatically patch your project with the following:'
  166. )}
  167. </p>
  168. <List symbol="bullet">
  169. <ListItem>
  170. {tct(
  171. 'Configure the SDK with your DSN and performance monitoring options in your [main:main.dart] file.',
  172. {
  173. main: <code />,
  174. }
  175. )}
  176. </ListItem>
  177. <ListItem>
  178. {tct(
  179. 'Update your [pubspec:pubspec.yaml] with the Sentry package',
  180. {
  181. pubspec: <code />,
  182. }
  183. )}
  184. </ListItem>
  185. <ListItem>
  186. {t('Add an example error to verify your setup')}
  187. </ListItem>
  188. </List>
  189. </Fragment>
  190. ),
  191. additionalInfo: tct(
  192. 'Alternatively, you can also [manualSetupLink:set up the SDK manually].',
  193. {
  194. manualSetupLink: (
  195. <ExternalLink href="https://docs.sentry.io/platforms/flutter/" />
  196. ),
  197. }
  198. ),
  199. },
  200. ],
  201. },
  202. ]
  203. : [
  204. {
  205. type: StepType.INSTALL,
  206. description: tct(
  207. 'Sentry captures data by using an SDK within your application. Add the following to your [pubspec:pubspec.yaml]',
  208. {
  209. pubspec: <code />,
  210. }
  211. ),
  212. configurations: [
  213. {
  214. code: [
  215. {
  216. label: 'YAML',
  217. value: 'yaml',
  218. language: 'yaml',
  219. filename: 'pubspec.yaml',
  220. partialLoading: params.sourcePackageRegistries?.isLoading,
  221. code: getManualInstallSnippet(params),
  222. },
  223. ],
  224. },
  225. ],
  226. },
  227. ],
  228. configure: params =>
  229. isAutoInstall(params)
  230. ? []
  231. : [
  232. {
  233. type: StepType.CONFIGURE,
  234. description: tct(
  235. 'Import [sentryFlutter: sentry_flutter] and initialize it in your [main:main.dart]',
  236. {
  237. sentryFlutter: <code />,
  238. main: <code />,
  239. }
  240. ),
  241. configurations: [
  242. ...(params.isProfilingSelected
  243. ? [
  244. {
  245. description: t(
  246. 'Flutter Profiling alpha is available for iOS and macOS since SDK version 7.12.0.'
  247. ),
  248. },
  249. ]
  250. : []),
  251. {
  252. code: [
  253. {
  254. label: 'Dart',
  255. value: 'dart',
  256. language: 'dart',
  257. filename: 'main.dart',
  258. code: getConfigureSnippet(params),
  259. },
  260. ],
  261. additionalInfo: params.isPerformanceSelected ? (
  262. <Fragment>
  263. <p>{configureAdditionalInfo}</p>
  264. <Alert type="info">
  265. {t(
  266. 'To monitor performance, you need to add extra instrumentation as described in the Tracing section below.'
  267. )}
  268. </Alert>
  269. </Fragment>
  270. ) : (
  271. configureAdditionalInfo
  272. ),
  273. },
  274. ],
  275. },
  276. ],
  277. verify: params =>
  278. isAutoInstall(params)
  279. ? []
  280. : [
  281. {
  282. type: StepType.VERIFY,
  283. description: t(
  284. 'Create an intentional error, so you can test that everything is working. In the example below, pressing the button will throw an exception:'
  285. ),
  286. configurations: [
  287. {
  288. code: [
  289. {
  290. label: 'Dart',
  291. value: 'dart',
  292. language: 'dart',
  293. code: getVerifySnippet(),
  294. },
  295. ],
  296. },
  297. ],
  298. },
  299. ...(params.isPerformanceSelected
  300. ? [
  301. {
  302. title: t('Tracing'),
  303. description: t(
  304. "You'll be able to monitor the performance of your app using the SDK. For example:"
  305. ),
  306. configurations: [
  307. {
  308. code: [
  309. {
  310. label: 'Dart',
  311. value: 'dart',
  312. language: 'dart',
  313. code: getPerformanceSnippet(),
  314. },
  315. ],
  316. additionalInfo: tct(
  317. 'To learn more about the API and automatic instrumentations, check out the [perfDocs: tracing documentation].',
  318. {
  319. perfDocs: (
  320. <ExternalLink href="https://docs.sentry.io/platforms/flutter/tracing/instrumentation/" />
  321. ),
  322. }
  323. ),
  324. },
  325. ],
  326. },
  327. ]
  328. : []),
  329. ],
  330. nextSteps: () => [
  331. {
  332. name: t('Upload Debug Symbols'),
  333. description: t(
  334. 'We offer a range of methods to provide Sentry with debug symbols so that you can see symbolicated stack traces and find the cause of your errors faster.'
  335. ),
  336. link: 'https://docs.sentry.io/platforms/flutter/upload-debug/',
  337. },
  338. {
  339. name: t('Distributed Tracing'),
  340. description: t(
  341. 'Connect all your services by configuring your endpoints in the Sentry init.'
  342. ),
  343. link: 'https://docs.sentry.io/platforms/flutter/tracing/trace-propagation/limiting-trace-propagation/',
  344. },
  345. {
  346. name: t('Connect your Git Repo'),
  347. description: t(
  348. 'Adding our Git integrations will allow us determine suspect commits, comment on PRs, and create links directly to your source code from Sentry issues.'
  349. ),
  350. link: 'https://docs.sentry.io/organization/integrations/source-code-mgmt/',
  351. },
  352. ],
  353. };
  354. const replayOnboarding: OnboardingConfig<PlatformOptions> = {
  355. install: params => [
  356. {
  357. type: StepType.INSTALL,
  358. description: tct(
  359. 'Make sure your Sentry Flutter SDK version is at least 8.9.0, which is required for Session Replay. You can update your [code:pubspec.yaml] to the matching version:',
  360. {code: <code />}
  361. ),
  362. configurations: [
  363. {
  364. code: [
  365. {
  366. label: 'YAML',
  367. value: 'yaml',
  368. language: 'yaml',
  369. code: getInstallSnippet(params),
  370. },
  371. ],
  372. },
  373. {
  374. description: t(
  375. 'To set up the integration, add the following to your Sentry initialization:'
  376. ),
  377. },
  378. {
  379. code: [
  380. {
  381. label: 'Dart',
  382. value: 'dart',
  383. language: 'dart',
  384. code: getInstallReplaySnippet(),
  385. },
  386. ],
  387. },
  388. ],
  389. },
  390. ],
  391. configure: () => [
  392. {
  393. type: StepType.CONFIGURE,
  394. description: getReplayMobileConfigureDescription({
  395. link: 'https://docs.sentry.io/platforms/flutter/session-replay/#privacy',
  396. }),
  397. configurations: [
  398. {
  399. description: t(
  400. 'The following code is the default configuration, which masks and blocks everything.'
  401. ),
  402. code: [
  403. {
  404. label: 'Dart',
  405. value: 'dart',
  406. language: 'dart',
  407. code: getConfigureReplaySnippet(),
  408. },
  409. ],
  410. },
  411. ],
  412. },
  413. ],
  414. verify: getReplayVerifyStep({
  415. replayOnErrorSampleRateName:
  416. 'options\u200b.experimental\u200b.sessionReplay\u200b.onErrorSampleRate',
  417. replaySessionSampleRateName:
  418. 'options\u200b.experimental\u200b.sessionReplay\u200b.sessionSampleRate',
  419. }),
  420. nextSteps: () => [],
  421. };
  422. const docs: Docs<PlatformOptions> = {
  423. onboarding,
  424. feedbackOnboardingCrashApi: feedbackOnboardingCrashApiDart,
  425. crashReportOnboarding: feedbackOnboardingCrashApiDart,
  426. platformOptions,
  427. replayOnboarding,
  428. };
  429. export default docs;