options.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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 'sentry/locale';
  15. import {Organization} from 'sentry/types';
  16. import {
  17. Dataset,
  18. EventTypes,
  19. SessionsAggregate,
  20. } from 'sentry/views/alerts/incidentRules/types';
  21. export type AlertType =
  22. | 'issues'
  23. | 'num_errors'
  24. | 'users_experiencing_errors'
  25. | 'throughput'
  26. | 'trans_duration'
  27. | 'apdex'
  28. | 'failure_rate'
  29. | 'lcp'
  30. | 'fid'
  31. | 'cls'
  32. | 'custom'
  33. | 'crash_free_sessions'
  34. | 'crash_free_users';
  35. export type MetricAlertType = Exclude<AlertType, 'issues'>;
  36. export const AlertWizardAlertNames: Record<AlertType, string> = {
  37. issues: t('Issues'),
  38. num_errors: t('Number of Errors'),
  39. users_experiencing_errors: t('Users Experiencing Errors'),
  40. throughput: t('Throughput'),
  41. trans_duration: t('Transaction Duration'),
  42. apdex: t('Apdex'),
  43. failure_rate: t('Failure Rate'),
  44. lcp: t('Largest Contentful Paint'),
  45. fid: t('First Input Delay'),
  46. cls: t('Cumulative Layout Shift'),
  47. custom: t('Custom Metric'),
  48. crash_free_sessions: t('Crash Free Session Rate'),
  49. crash_free_users: t('Crash Free User Rate'),
  50. };
  51. type AlertWizardCategory = {
  52. categoryHeading: string;
  53. options: AlertType[];
  54. };
  55. export const getAlertWizardCategories = (org: Organization): AlertWizardCategory[] => [
  56. {
  57. categoryHeading: t('Errors'),
  58. options: ['issues', 'num_errors', 'users_experiencing_errors'],
  59. },
  60. ...(org.features.includes('crash-rate-alerts')
  61. ? [
  62. {
  63. categoryHeading: t('Sessions'),
  64. options: ['crash_free_sessions', 'crash_free_users'] as AlertType[],
  65. },
  66. ]
  67. : []),
  68. {
  69. categoryHeading: t('Performance'),
  70. options: [
  71. 'throughput',
  72. 'trans_duration',
  73. 'apdex',
  74. 'failure_rate',
  75. 'lcp',
  76. 'fid',
  77. 'cls',
  78. ],
  79. },
  80. {
  81. categoryHeading: t('Other'),
  82. options: ['custom'],
  83. },
  84. ];
  85. type PanelContent = {
  86. description: string;
  87. examples: string[];
  88. docsLink?: string;
  89. illustration?: string;
  90. };
  91. export const AlertWizardPanelContent: Record<AlertType, PanelContent> = {
  92. issues: {
  93. description: t(
  94. '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.'
  95. ),
  96. examples: [
  97. t("When the triggering event's level is fatal."),
  98. t('When an issue was seen 100 times in the last 2 days.'),
  99. t(
  100. 'Create a JIRA ticket when an issue changes state from resolved to unresolved and is unassigned.'
  101. ),
  102. ],
  103. illustration: diagramIssues,
  104. },
  105. num_errors: {
  106. description: t(
  107. '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.'
  108. ),
  109. examples: [
  110. t('When the signup page has more than 10k errors in 5 minutes.'),
  111. t('When there are more than 500k errors in 10 minutes from a specific file.'),
  112. ],
  113. illustration: diagramErrors,
  114. },
  115. users_experiencing_errors: {
  116. description: t(
  117. 'Alert when the number of users affected by errors in your project crosses a threshold.'
  118. ),
  119. examples: [
  120. t('When 100k users experience an error in 1 hour.'),
  121. t('When 100 users experience a problem on the Checkout page.'),
  122. ],
  123. illustration: diagramUsers,
  124. },
  125. throughput: {
  126. description: t(
  127. 'Throughput is the total number of transactions in a project and you can alert when it reaches a threshold within a period of time.'
  128. ),
  129. examples: [
  130. t('When number of transactions on a key page exceeds 100k per minute.'),
  131. t('When number of transactions drops below a threshold.'),
  132. ],
  133. illustration: diagramThroughput,
  134. },
  135. trans_duration: {
  136. description: t(
  137. 'Monitor how long it takes for transactions to complete. Use flexible aggregates like percentiles, averages, and min/max.'
  138. ),
  139. examples: [
  140. t('When any transaction is slower than 3 seconds.'),
  141. t('When the 75th percentile response time is higher than 250 milliseconds.'),
  142. ],
  143. illustration: diagramTransactionDuration,
  144. },
  145. apdex: {
  146. description: t(
  147. '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.'
  148. ),
  149. examples: [t('When apdex is below 300.')],
  150. docsLink: 'https://docs.sentry.io/product/performance/metrics/#apdex',
  151. illustration: diagramApdex,
  152. },
  153. failure_rate: {
  154. description: t(
  155. 'Failure rate is the percentage of unsuccessful transactions. Sentry treats transactions with a status other than “ok,” “canceled,” and “unknown” as failures.'
  156. ),
  157. examples: [t('When the failure rate for an important endpoint reaches 10%.')],
  158. docsLink: 'https://docs.sentry.io/product/performance/metrics/#failure-rate',
  159. illustration: diagramFailureRate,
  160. },
  161. lcp: {
  162. description: t(
  163. '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.'
  164. ),
  165. examples: [
  166. t('When the 75th percentile LCP of your homepage is longer than 2.5 seconds.'),
  167. ],
  168. docsLink: 'https://docs.sentry.io/product/performance/web-vitals',
  169. illustration: diagramLCP,
  170. },
  171. fid: {
  172. description: t(
  173. '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.'
  174. ),
  175. examples: [t('When the average FID of a page is longer than 4 seconds.')],
  176. docsLink: 'https://docs.sentry.io/product/performance/web-vitals',
  177. illustration: diagramFID,
  178. },
  179. cls: {
  180. description: t(
  181. '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.'
  182. ),
  183. examples: [t('When the CLS of a page is more than 0.5.')],
  184. docsLink: 'https://docs.sentry.io/product/performance/web-vitals',
  185. illustration: diagramCLS,
  186. },
  187. custom: {
  188. description: t(
  189. 'Alert on metrics which are not listed above, such as first paint (FP), first contentful paint (FCP), and time to first byte (TTFB).'
  190. ),
  191. examples: [
  192. t('When the 95th percentile FP of a page is longer than 250 milliseconds.'),
  193. t('When the average TTFB of a page is longer than 600 millliseconds.'),
  194. ],
  195. illustration: diagramCustom,
  196. },
  197. crash_free_sessions: {
  198. description: t(
  199. '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.'
  200. ),
  201. examples: [
  202. t('When the Crash Free Rate is below 98%, send a Slack notification to the team.'),
  203. ],
  204. illustration: diagramCrashFreeSessions,
  205. },
  206. crash_free_users: {
  207. description: t(
  208. '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.'
  209. ),
  210. examples: [
  211. t('When the Crash Free Rate is below 97%, send an email notification to yourself.'),
  212. ],
  213. illustration: diagramCrashFreeUsers,
  214. },
  215. };
  216. export type WizardRuleTemplate = {
  217. aggregate: string;
  218. dataset: Dataset;
  219. eventTypes: EventTypes;
  220. };
  221. export const AlertWizardRuleTemplates: Record<
  222. MetricAlertType,
  223. Readonly<WizardRuleTemplate>
  224. > = {
  225. num_errors: {
  226. aggregate: 'count()',
  227. dataset: Dataset.ERRORS,
  228. eventTypes: EventTypes.ERROR,
  229. },
  230. users_experiencing_errors: {
  231. aggregate: 'count_unique(user)',
  232. dataset: Dataset.ERRORS,
  233. eventTypes: EventTypes.ERROR,
  234. },
  235. throughput: {
  236. aggregate: 'count()',
  237. dataset: Dataset.TRANSACTIONS,
  238. eventTypes: EventTypes.TRANSACTION,
  239. },
  240. trans_duration: {
  241. aggregate: 'p95(transaction.duration)',
  242. dataset: Dataset.TRANSACTIONS,
  243. eventTypes: EventTypes.TRANSACTION,
  244. },
  245. apdex: {
  246. aggregate: 'apdex(300)',
  247. dataset: Dataset.TRANSACTIONS,
  248. eventTypes: EventTypes.TRANSACTION,
  249. },
  250. failure_rate: {
  251. aggregate: 'failure_rate()',
  252. dataset: Dataset.TRANSACTIONS,
  253. eventTypes: EventTypes.TRANSACTION,
  254. },
  255. lcp: {
  256. aggregate: 'p95(measurements.lcp)',
  257. dataset: Dataset.TRANSACTIONS,
  258. eventTypes: EventTypes.TRANSACTION,
  259. },
  260. fid: {
  261. aggregate: 'p95(measurements.fid)',
  262. dataset: Dataset.TRANSACTIONS,
  263. eventTypes: EventTypes.TRANSACTION,
  264. },
  265. cls: {
  266. aggregate: 'p95(measurements.cls)',
  267. dataset: Dataset.TRANSACTIONS,
  268. eventTypes: EventTypes.TRANSACTION,
  269. },
  270. custom: {
  271. aggregate: 'p95(measurements.fp)',
  272. dataset: Dataset.TRANSACTIONS,
  273. eventTypes: EventTypes.TRANSACTION,
  274. },
  275. crash_free_sessions: {
  276. aggregate: SessionsAggregate.CRASH_FREE_SESSIONS,
  277. // TODO(scttcper): Use Dataset.Metric on GA of alert-crash-free-metrics
  278. dataset: Dataset.SESSIONS,
  279. eventTypes: EventTypes.SESSION,
  280. },
  281. crash_free_users: {
  282. aggregate: SessionsAggregate.CRASH_FREE_USERS,
  283. // TODO(scttcper): Use Dataset.Metric on GA of alert-crash-free-metrics
  284. dataset: Dataset.SESSIONS,
  285. eventTypes: EventTypes.USER,
  286. },
  287. };
  288. export const DEFAULT_WIZARD_TEMPLATE = AlertWizardRuleTemplates.num_errors;
  289. export const hidePrimarySelectorSet = new Set<AlertType>([
  290. 'num_errors',
  291. 'users_experiencing_errors',
  292. 'throughput',
  293. 'apdex',
  294. 'failure_rate',
  295. 'crash_free_sessions',
  296. 'crash_free_users',
  297. ]);
  298. export const hideParameterSelectorSet = new Set<AlertType>([
  299. 'trans_duration',
  300. 'lcp',
  301. 'fid',
  302. 'cls',
  303. ]);
  304. export function getFunctionHelpText(alertType: AlertType): {
  305. labelText: string;
  306. timeWindowText?: string;
  307. } {
  308. const timeWindowText = t('over');
  309. if (alertType === 'apdex') {
  310. return {
  311. labelText: t('Select apdex threshold and time interval'),
  312. timeWindowText,
  313. };
  314. }
  315. if (hidePrimarySelectorSet.has(alertType)) {
  316. return {
  317. labelText: t('Select time interval'),
  318. };
  319. }
  320. return {
  321. labelText: t('Select function and time interval'),
  322. timeWindowText,
  323. };
  324. }