options.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. import diagramApdex from 'sentry-images/spot/alerts-wizard-apdex.svg';
  2. import diagramCLS from 'sentry-images/spot/alerts-wizard-cls.svg';
  3. import diagramCrashFreeSessions from 'sentry-images/spot/alerts-wizard-crash-free-sessions.svg';
  4. import diagramCrashFreeUsers from 'sentry-images/spot/alerts-wizard-crash-free-users.svg';
  5. import diagramCustom from 'sentry-images/spot/alerts-wizard-custom.svg';
  6. import diagramErrors from 'sentry-images/spot/alerts-wizard-errors.svg';
  7. import diagramFailureRate from 'sentry-images/spot/alerts-wizard-failure-rate.svg';
  8. import diagramFID from 'sentry-images/spot/alerts-wizard-fid.svg';
  9. import diagramIssues from 'sentry-images/spot/alerts-wizard-issues.svg';
  10. import diagramLCP from 'sentry-images/spot/alerts-wizard-lcp.svg';
  11. import diagramThroughput from 'sentry-images/spot/alerts-wizard-throughput.svg';
  12. import diagramTransactionDuration from 'sentry-images/spot/alerts-wizard-transaction-duration.svg';
  13. import diagramUsers from 'sentry-images/spot/alerts-wizard-users-experiencing-errors.svg';
  14. import {t} from 'app/locale';
  15. import {Organization} from 'app/types';
  16. import {Dataset, EventTypes} from 'app/views/alerts/incidentRules/types';
  17. export type AlertType =
  18. | 'issues'
  19. | 'num_errors'
  20. | 'users_experiencing_errors'
  21. | 'throughput'
  22. | 'trans_duration'
  23. | 'apdex'
  24. | 'failure_rate'
  25. | 'lcp'
  26. | 'fid'
  27. | 'cls'
  28. | 'custom'
  29. | 'crash_free_sessions'
  30. | 'crash_free_users';
  31. export const WebVitalAlertTypes = new Set(['lcp', 'fid', 'cls', 'fcp']);
  32. export const AlertWizardAlertNames: Record<AlertType, string> = {
  33. issues: t('Issues'),
  34. num_errors: t('Number of Errors'),
  35. users_experiencing_errors: t('Users Experiencing Errors'),
  36. throughput: t('Throughput'),
  37. trans_duration: t('Transaction Duration'),
  38. apdex: t('Apdex'),
  39. failure_rate: t('Failure Rate'),
  40. lcp: t('Largest Contentful Paint'),
  41. fid: t('First Input Delay'),
  42. cls: t('Cumulative Layout Shift'),
  43. custom: t('Custom Metric'),
  44. crash_free_sessions: t('Crash Free Session Rate'),
  45. crash_free_users: t('Crash Free User Rate'),
  46. };
  47. type AlertWizardCategory = {categoryHeading: string; options: AlertType[]};
  48. export const getAlertWizardCategories = (org: Organization): AlertWizardCategory[] => [
  49. {
  50. categoryHeading: t('Errors'),
  51. options: ['issues', 'num_errors', 'users_experiencing_errors'],
  52. },
  53. ...(org.features.includes('crash-rate-alerts')
  54. ? [
  55. {
  56. categoryHeading: t('Sessions'),
  57. options: ['crash_free_sessions', 'crash_free_users'] as AlertType[],
  58. },
  59. ]
  60. : []),
  61. {
  62. categoryHeading: t('Performance'),
  63. options: [
  64. 'throughput',
  65. 'trans_duration',
  66. 'apdex',
  67. 'failure_rate',
  68. 'lcp',
  69. 'fid',
  70. 'cls',
  71. ],
  72. },
  73. {
  74. categoryHeading: t('Other'),
  75. options: ['custom'],
  76. },
  77. ];
  78. type PanelContent = {
  79. description: string;
  80. docsLink?: string;
  81. examples: string[];
  82. illustration?: string;
  83. };
  84. export const AlertWizardPanelContent: Record<AlertType, PanelContent> = {
  85. issues: {
  86. description: t(
  87. 'Issues are groups of errors that have a similar stacktrace. Set an alert for new issues, when an issue changes state, frequency of errors, or users affected by an issue.'
  88. ),
  89. examples: [
  90. t("When the triggering event's level is fatal."),
  91. t('When an issue was seen 100 times in the last 2 days.'),
  92. t(
  93. 'Create a JIRA ticket when an issue changes state from resolved to unresolved and is unassigned.'
  94. ),
  95. ],
  96. illustration: diagramIssues,
  97. },
  98. num_errors: {
  99. description: t(
  100. 'Alert when the number of errors in a project matching your filters crosses a threshold. This is useful for monitoring the overall level or errors in your project or errors occurring in specific parts of your app.'
  101. ),
  102. examples: [
  103. t('When the signup page has more than 10k errors in 5 minutes.'),
  104. t('When there are more than 500k errors in 10 minutes from a specific file.'),
  105. ],
  106. illustration: diagramErrors,
  107. },
  108. users_experiencing_errors: {
  109. description: t(
  110. 'Alert when the number of users affected by errors in your project crosses a threshold.'
  111. ),
  112. examples: [
  113. t('When 100k users experience an error in 1 hour.'),
  114. t('When 100 users experience a problem on the Checkout page.'),
  115. ],
  116. illustration: diagramUsers,
  117. },
  118. throughput: {
  119. description: t(
  120. 'Throughput is the total number of transactions in a project and you can alert when it reaches a threshold within a period of time.'
  121. ),
  122. examples: [
  123. t('When number of transactions on a key page exceeds 100k per minute.'),
  124. t('When number of transactions drops below a threshold.'),
  125. ],
  126. illustration: diagramThroughput,
  127. },
  128. trans_duration: {
  129. description: t(
  130. 'Monitor how long it takes for transactions to complete. Use flexible aggregates like percentiles, averages, and min/max.'
  131. ),
  132. examples: [
  133. t('When any transaction is slower than 3 seconds.'),
  134. t('When the 75th percentile response time is higher than 250 milliseconds.'),
  135. ],
  136. illustration: diagramTransactionDuration,
  137. },
  138. apdex: {
  139. description: t(
  140. 'Apdex is a metric used to track and measure user satisfaction based on your application response times. The Apdex score provides the ratio of satisfactory, tolerable, and frustrated requests in a specific transaction or endpoint.'
  141. ),
  142. examples: [t('When apdex is below 300.')],
  143. docsLink: 'https://docs.sentry.io/product/performance/metrics/#apdex',
  144. illustration: diagramApdex,
  145. },
  146. failure_rate: {
  147. description: t(
  148. 'Failure rate is the percentage of unsuccessful transactions. Sentry treats transactions with a status other than “ok,” “canceled,” and “unknown” as failures.'
  149. ),
  150. examples: [t('When the failure rate for an important endpoint reaches 10%.')],
  151. docsLink: 'https://docs.sentry.io/product/performance/metrics/#failure-rate',
  152. illustration: diagramFailureRate,
  153. },
  154. lcp: {
  155. description: t(
  156. 'Largest Contentful Paint (LCP) measures loading performance. It marks the point when the largest image or text block is visible within the viewport. A fast LCP helps reassure the user that the page is useful, and so we recommend an LCP of less than 2.5 seconds.'
  157. ),
  158. examples: [
  159. t('When the 75th percentile LCP of your homepage is longer than 2.5 seconds.'),
  160. ],
  161. docsLink: 'https://docs.sentry.io/product/performance/web-vitals',
  162. illustration: diagramLCP,
  163. },
  164. fid: {
  165. description: t(
  166. 'First Input Delay (FID) measures interactivity as the response time when the user tries to interact with the viewport. A low FID helps ensure that a page is useful, and we recommend a FID of less than 100 milliseconds.'
  167. ),
  168. examples: [t('When the average FID of a page is longer than 4 seconds.')],
  169. docsLink: 'https://docs.sentry.io/product/performance/web-vitals',
  170. illustration: diagramFID,
  171. },
  172. cls: {
  173. description: t(
  174. 'Cumulative Layout Shift (CLS) measures visual stability by quantifying unexpected layout shifts that occur during the entire lifespan of the page. A CLS of less than 0.1 is a good user experience, while anything greater than 0.25 is poor.'
  175. ),
  176. examples: [t('When the CLS of a page is more than 0.5.')],
  177. docsLink: 'https://docs.sentry.io/product/performance/web-vitals',
  178. illustration: diagramCLS,
  179. },
  180. custom: {
  181. description: t(
  182. 'Alert on metrics which are not listed above, such as first paint (FP), first contentful paint (FCP), and time to first byte (TTFB).'
  183. ),
  184. examples: [
  185. t('When the 95th percentile FP of a page is longer than 250 milliseconds.'),
  186. t('When the average TTFB of a page is longer than 600 millliseconds.'),
  187. ],
  188. illustration: diagramCustom,
  189. },
  190. crash_free_sessions: {
  191. description: t(
  192. 'A session begins when a user starts the application and ends when it’s closed or sent to the background. A crash is when a session ends due to an error and this type of alert lets you monitor when those crashed sessions exceed a threshold. This lets you get a better picture of the health of your app.'
  193. ),
  194. examples: [
  195. t('When the Crash Free Rate is below 98%, send a Slack notification to the team.'),
  196. ],
  197. illustration: diagramCrashFreeSessions,
  198. },
  199. crash_free_users: {
  200. description: t(
  201. 'Crash Free Users is the percentage of distinct users that haven’t experienced a crash and so this type of alert tells you when the overall user experience dips below a certain unacceptable threshold.'
  202. ),
  203. examples: [
  204. t('When the Crash Free Rate is below 97%, send an email notification to yourself.'),
  205. ],
  206. illustration: diagramCrashFreeUsers,
  207. },
  208. };
  209. export type WizardRuleTemplate = {
  210. aggregate: string;
  211. dataset: Dataset;
  212. eventTypes: EventTypes;
  213. };
  214. export const AlertWizardRuleTemplates: Record<
  215. Exclude<AlertType, 'issues' | 'crash_free_sessions' | 'crash_free_users'>,
  216. WizardRuleTemplate
  217. > = {
  218. num_errors: {
  219. aggregate: 'count()',
  220. dataset: Dataset.ERRORS,
  221. eventTypes: EventTypes.ERROR,
  222. },
  223. users_experiencing_errors: {
  224. aggregate: 'count_unique(tags[sentry:user])',
  225. dataset: Dataset.ERRORS,
  226. eventTypes: EventTypes.ERROR,
  227. },
  228. throughput: {
  229. aggregate: 'count()',
  230. dataset: Dataset.TRANSACTIONS,
  231. eventTypes: EventTypes.TRANSACTION,
  232. },
  233. trans_duration: {
  234. aggregate: 'p95(transaction.duration)',
  235. dataset: Dataset.TRANSACTIONS,
  236. eventTypes: EventTypes.TRANSACTION,
  237. },
  238. apdex: {
  239. aggregate: 'apdex(300)',
  240. dataset: Dataset.TRANSACTIONS,
  241. eventTypes: EventTypes.TRANSACTION,
  242. },
  243. failure_rate: {
  244. aggregate: 'failure_rate()',
  245. dataset: Dataset.TRANSACTIONS,
  246. eventTypes: EventTypes.TRANSACTION,
  247. },
  248. lcp: {
  249. aggregate: 'p95(measurements.lcp)',
  250. dataset: Dataset.TRANSACTIONS,
  251. eventTypes: EventTypes.TRANSACTION,
  252. },
  253. fid: {
  254. aggregate: 'p95(measurements.fid)',
  255. dataset: Dataset.TRANSACTIONS,
  256. eventTypes: EventTypes.TRANSACTION,
  257. },
  258. cls: {
  259. aggregate: 'p95(measurements.cls)',
  260. dataset: Dataset.TRANSACTIONS,
  261. eventTypes: EventTypes.TRANSACTION,
  262. },
  263. custom: {
  264. aggregate: 'p95(measurements.fp)',
  265. dataset: Dataset.TRANSACTIONS,
  266. eventTypes: EventTypes.TRANSACTION,
  267. },
  268. };
  269. export const hidePrimarySelectorSet = new Set<AlertType>([
  270. 'num_errors',
  271. 'users_experiencing_errors',
  272. 'throughput',
  273. 'apdex',
  274. 'failure_rate',
  275. ]);
  276. export const hideParameterSelectorSet = new Set<AlertType>([
  277. 'trans_duration',
  278. 'lcp',
  279. 'fid',
  280. 'cls',
  281. ]);
  282. export function getFunctionHelpText(alertType: AlertType): {
  283. labelText: string;
  284. timeWindowText?: string;
  285. } {
  286. const timeWindowText = t('over');
  287. if (alertType === 'apdex') {
  288. return {
  289. labelText: t('Select apdex value and time interval'),
  290. timeWindowText,
  291. };
  292. } else if (hidePrimarySelectorSet.has(alertType)) {
  293. return {
  294. labelText: t('Select time interval'),
  295. };
  296. } else {
  297. return {
  298. labelText: t('Select function and time interval'),
  299. timeWindowText,
  300. };
  301. }
  302. }