angular.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import {Fragment} from 'react';
  2. import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout';
  3. import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
  4. import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
  5. import {
  6. type Configuration,
  7. StepType,
  8. } from 'sentry/components/onboarding/gettingStartedDoc/step';
  9. import type {
  10. BasePlatformOptions,
  11. Docs,
  12. DocsParams,
  13. OnboardingConfig,
  14. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  15. import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
  16. import {
  17. getCrashReportJavaScriptInstallStep,
  18. getCrashReportModalConfigDescription,
  19. getCrashReportModalIntroduction,
  20. getFeedbackConfigOptions,
  21. getFeedbackConfigureDescription,
  22. } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
  23. import {getJSMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
  24. import {
  25. getProfilingDocumentHeaderConfigurationStep,
  26. MaybeBrowserProfilingBetaWarning,
  27. } from 'sentry/components/onboarding/gettingStartedDoc/utils/profilingOnboarding';
  28. import {
  29. getReplayConfigOptions,
  30. getReplayConfigureDescription,
  31. getReplayVerifyStep,
  32. } from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
  33. import {featureFlagOnboarding} from 'sentry/gettingStartedDocs/javascript/javascript';
  34. import {t, tct} from 'sentry/locale';
  35. export enum AngularConfigType {
  36. APP = 'standalone',
  37. MODULE = 'module',
  38. }
  39. const platformOptions = {
  40. configType: {
  41. label: t('Config Type'),
  42. defaultValue: AngularConfigType.APP,
  43. items: [
  44. {
  45. label: 'App Config',
  46. value: AngularConfigType.APP,
  47. },
  48. {
  49. label: 'NGModule Config',
  50. value: AngularConfigType.MODULE,
  51. },
  52. ],
  53. },
  54. } satisfies BasePlatformOptions;
  55. type PlatformOptions = typeof platformOptions;
  56. type Params = DocsParams<PlatformOptions>;
  57. function isModuleConfig(params: Params) {
  58. return params.platformOptions.configType === AngularConfigType.MODULE;
  59. }
  60. function getSdkSetupSnippet(params: Params) {
  61. const imports = isModuleConfig(params)
  62. ? `
  63. import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
  64. import * as Sentry from "@sentry/angular";
  65. import { AppModule } from "./app/app.module";`
  66. : `
  67. import { bootstrapApplication } from '@angular/platform-browser';
  68. import * as Sentry from "@sentry/angular";
  69. import { appConfig } from './app/app.config';
  70. import { AppComponent } from './app/app.component';
  71. `;
  72. const appInit = isModuleConfig(params)
  73. ? `
  74. platformBrowserDynamic()
  75. .bootstrapModule(AppModule)
  76. .catch((err) => console.error(err));`
  77. : `
  78. bootstrapApplication(appConfig, AppComponent)
  79. .catch((err) => console.error(err));`;
  80. return `${imports.trim()}
  81. Sentry.init({
  82. dsn: "${params.dsn.public}",
  83. integrations: [${
  84. params.isPerformanceSelected
  85. ? `
  86. Sentry.browserTracingIntegration(),`
  87. : ''
  88. }${
  89. params.isProfilingSelected
  90. ? `
  91. Sentry.browserProfilingIntegration(),`
  92. : ''
  93. }${
  94. params.isFeedbackSelected
  95. ? `
  96. Sentry.feedbackIntegration({
  97. // Additional SDK configuration goes in here, for example:
  98. colorScheme: "system",
  99. ${getFeedbackConfigOptions(params.feedbackOptions)}}),`
  100. : ''
  101. }${
  102. params.isReplaySelected
  103. ? `
  104. Sentry.replayIntegration(${getReplayConfigOptions(params.replayOptions)}),`
  105. : ''
  106. }
  107. ],${
  108. params.isPerformanceSelected
  109. ? `
  110. // Tracing
  111. tracesSampleRate: 1.0, // Capture 100% of the transactions
  112. // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
  113. tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/],`
  114. : ''
  115. }${
  116. params.isReplaySelected
  117. ? `
  118. // Session Replay
  119. 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.
  120. replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.`
  121. : ''
  122. }${
  123. params.isProfilingSelected
  124. ? `
  125. // Set profilesSampleRate to 1.0 to profile every transaction.
  126. // Since profilesSampleRate is relative to tracesSampleRate,
  127. // the final profiling rate can be computed as tracesSampleRate * profilesSampleRate
  128. // For example, a tracesSampleRate of 0.5 and profilesSampleRate of 0.5 would
  129. // results in 25% of transactions being profiled (0.5*0.5=0.25)
  130. profilesSampleRate: 1.0,`
  131. : ''
  132. }
  133. });
  134. ${appInit.trim()}`;
  135. }
  136. const getConfigureAppModuleSnippet = () => `
  137. import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core";
  138. import { Router } from "@angular/router";
  139. import * as Sentry from "@sentry/angular";
  140. @NgModule({
  141. // ...
  142. providers: [
  143. {
  144. provide: ErrorHandler,
  145. useValue: Sentry.createErrorHandler({
  146. showDialog: true,
  147. }),
  148. }, {
  149. provide: Sentry.TraceService,
  150. deps: [Router],
  151. },
  152. {
  153. provide: APP_INITIALIZER,
  154. useFactory: () => () => {},
  155. deps: [Sentry.TraceService],
  156. multi: true,
  157. },
  158. ],
  159. })
  160. export class AppModule {}
  161. `;
  162. const getConfigureAppConfigSnippet = () => `
  163. import { APP_INITIALIZER, ApplicationConfig, ErrorHandler } from '@angular/core';
  164. import { Router } from '@angular/router';
  165. import * as Sentry from "@sentry/angular";
  166. export const appConfig: ApplicationConfig = {
  167. providers: [
  168. // ...
  169. {
  170. provide: ErrorHandler,
  171. useValue: Sentry.createErrorHandler(),
  172. },
  173. {
  174. provide: Sentry.TraceService,
  175. deps: [Router],
  176. },
  177. {
  178. provide: APP_INITIALIZER,
  179. useFactory: () => () => {},
  180. deps: [Sentry.TraceService],
  181. multi: true,
  182. },
  183. ]
  184. };
  185. `;
  186. const getVerifySnippetTemplate = () => `
  187. <button (click)="throwTestError()">Test Sentry Error</button>
  188. `;
  189. const getVerifySnippetComponent = () => `
  190. public throwTestError(): void {
  191. throw new Error("Sentry Test Error");
  192. }`;
  193. function getVerifyConfiguration(): Configuration {
  194. return {
  195. description: t(
  196. 'To verify that everything is working as expected, you can trigger a test error in your app. As an example we will add a button that throws an error when being clicked to your main app component.'
  197. ),
  198. configurations: [
  199. {
  200. description: tct(
  201. 'First add the button element to your [code:app.component.html]:',
  202. {code: <code />}
  203. ),
  204. code: [
  205. {
  206. label: 'HTML',
  207. value: 'html',
  208. language: 'html',
  209. filename: 'app.component.html',
  210. code: getVerifySnippetTemplate(),
  211. },
  212. ],
  213. },
  214. {
  215. description: tct('Then, in your [code:app.component.ts] add the event handler:', {
  216. code: <code />,
  217. }),
  218. code: [
  219. {
  220. label: 'TypeScript',
  221. value: 'typescript',
  222. language: 'typescript',
  223. filename: 'app.component.ts',
  224. code: getVerifySnippetComponent(),
  225. },
  226. ],
  227. },
  228. ],
  229. };
  230. }
  231. const getInstallConfig = () => [
  232. {
  233. language: 'bash',
  234. code: [
  235. {
  236. label: 'npm',
  237. value: 'npm',
  238. language: 'bash',
  239. code: `npm install --save @sentry/angular`,
  240. },
  241. {
  242. label: 'yarn',
  243. value: 'yarn',
  244. language: 'bash',
  245. code: `yarn add @sentry/angular`,
  246. },
  247. {
  248. label: 'pnpm',
  249. value: 'pnpm',
  250. language: 'bash',
  251. code: `pnpm install @sentry/angular`,
  252. },
  253. ],
  254. },
  255. ];
  256. const onboarding: OnboardingConfig<PlatformOptions> = {
  257. introduction: params => (
  258. <Fragment>
  259. <MaybeBrowserProfilingBetaWarning {...params} />
  260. <p>
  261. {tct(
  262. 'In this quick guide you’ll use [strong:npm], [strong:yarn] or [strong:pnpm] to set up:',
  263. {
  264. strong: <strong />,
  265. }
  266. )}
  267. </p>
  268. </Fragment>
  269. ),
  270. install: () => [
  271. {
  272. type: StepType.INSTALL,
  273. description: tct(
  274. 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:',
  275. {code: <code />}
  276. ),
  277. configurations: getInstallConfig(),
  278. },
  279. ],
  280. configure: (params: Params) => [
  281. {
  282. type: StepType.CONFIGURE,
  283. configurations: [
  284. {
  285. description: tct(
  286. `Initialize the Sentry Angular SDK in your [code:main.ts] file as early as possible, before initializing Angular:`,
  287. {
  288. code: <code />,
  289. }
  290. ),
  291. language: 'javascript',
  292. code: getSdkSetupSnippet(params),
  293. },
  294. {
  295. description: isModuleConfig(params)
  296. ? tct(
  297. "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.module.ts] file:",
  298. {code: <code />}
  299. )
  300. : tct(
  301. "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.config.ts] file:",
  302. {code: <code />}
  303. ),
  304. language: 'javascript',
  305. code: isModuleConfig(params)
  306. ? getConfigureAppModuleSnippet()
  307. : getConfigureAppConfigSnippet(),
  308. },
  309. ...(params.isProfilingSelected
  310. ? [getProfilingDocumentHeaderConfigurationStep()]
  311. : []),
  312. ],
  313. },
  314. getUploadSourceMapsStep({
  315. guideLink: 'https://docs.sentry.io/platforms/javascript/guides/angular/sourcemaps/',
  316. ...params,
  317. }),
  318. ],
  319. verify: () => [
  320. {
  321. type: StepType.VERIFY,
  322. configurations: [
  323. getVerifyConfiguration(),
  324. {
  325. description: t(
  326. "After clicking the button, you should see the error on Sentry's Issues page."
  327. ),
  328. },
  329. ],
  330. },
  331. ],
  332. nextSteps: () => [
  333. {
  334. id: 'angular-features',
  335. name: t('Angular Features'),
  336. description: t(
  337. 'Learn about our first class integration with the Angular framework.'
  338. ),
  339. link: 'https://docs.sentry.io/platforms/javascript/guides/angular/features/',
  340. },
  341. ],
  342. };
  343. const replayOnboarding: OnboardingConfig<PlatformOptions> = {
  344. install: () => [
  345. {
  346. type: StepType.INSTALL,
  347. description: tct(
  348. 'In order to use Session Replay, you will need version 7.27.0 of [code:@sentry/angular] at minimum. You do not need to install any additional packages.',
  349. {
  350. code: <code />,
  351. }
  352. ),
  353. configurations: getInstallConfig(),
  354. },
  355. ],
  356. configure: (params: Params) => [
  357. {
  358. type: StepType.CONFIGURE,
  359. description: getReplayConfigureDescription({
  360. link: 'https://docs.sentry.io/platforms/javascript/guides/angular/session-replay/',
  361. }),
  362. configurations: [
  363. {
  364. code: [
  365. {
  366. label: 'JavaScript',
  367. value: 'javascript',
  368. language: 'javascript',
  369. code: getSdkSetupSnippet(params),
  370. },
  371. ],
  372. },
  373. ],
  374. additionalInfo: <TracePropagationMessage />,
  375. },
  376. ],
  377. verify: getReplayVerifyStep(),
  378. nextSteps: () => [],
  379. };
  380. const feedbackOnboarding: OnboardingConfig<PlatformOptions> = {
  381. install: () => [
  382. {
  383. type: StepType.INSTALL,
  384. description: tct(
  385. 'For the User Feedback integration to work, you must have the Sentry browser SDK package, or an equivalent framework SDK (e.g. [code:@sentry/angular]) installed, minimum version 7.85.0.',
  386. {
  387. code: <code />,
  388. }
  389. ),
  390. configurations: getInstallConfig(),
  391. },
  392. ],
  393. configure: (params: Params) => [
  394. {
  395. type: StepType.CONFIGURE,
  396. description: getFeedbackConfigureDescription({
  397. linkConfig:
  398. 'https://docs.sentry.io/platforms/javascript/guides/angular/user-feedback/configuration/',
  399. linkButton:
  400. 'https://docs.sentry.io/platforms/javascript/guides/angular/user-feedback/configuration/#bring-your-own-button',
  401. }),
  402. configurations: [
  403. {
  404. code: [
  405. {
  406. label: 'JavaScript',
  407. value: 'javascript',
  408. language: 'javascript',
  409. code: getSdkSetupSnippet(params),
  410. },
  411. ],
  412. },
  413. ],
  414. additionalInfo: crashReportCallout({
  415. link: 'https://docs.sentry.io/platforms/javascript/guides/angular/user-feedback/#crash-report-modal',
  416. }),
  417. },
  418. ],
  419. verify: () => [],
  420. nextSteps: () => [],
  421. };
  422. const crashReportOnboarding: OnboardingConfig<PlatformOptions> = {
  423. introduction: () => getCrashReportModalIntroduction(),
  424. install: (params: Params) => getCrashReportJavaScriptInstallStep(params),
  425. configure: () => [
  426. {
  427. type: StepType.CONFIGURE,
  428. description: getCrashReportModalConfigDescription({
  429. link: 'https://docs.sentry.io/platforms/javascript/guides/angular/user-feedback/configuration/#crash-report-modal',
  430. }),
  431. additionalInfo: widgetCallout({
  432. link: 'https://docs.sentry.io/platforms/javascript/guides/angular/user-feedback/#user-feedback-widget',
  433. }),
  434. },
  435. ],
  436. verify: () => [],
  437. nextSteps: () => [],
  438. };
  439. const profilingOnboarding: OnboardingConfig<PlatformOptions> = {
  440. ...onboarding,
  441. introduction: params => <MaybeBrowserProfilingBetaWarning {...params} />,
  442. };
  443. const docs: Docs<PlatformOptions> = {
  444. onboarding,
  445. feedbackOnboardingNpm: feedbackOnboarding,
  446. replayOnboarding,
  447. customMetricsOnboarding: getJSMetricsOnboarding({getInstallConfig}),
  448. crashReportOnboarding,
  449. platformOptions,
  450. profilingOnboarding,
  451. featureFlagOnboarding: featureFlagOnboarding,
  452. };
  453. export default docs;