angular.tsx 14 KB

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