presets.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import {Client} from 'sentry/api';
  2. import {IconGraph} from 'sentry/icons';
  3. import {t} from 'sentry/locale';
  4. import {Organization, Project} from 'sentry/types';
  5. import {uniqueId} from 'sentry/utils/guid';
  6. import {AlertType} from '../../wizard/options';
  7. import {
  8. ActionType,
  9. AlertRuleComparisonType,
  10. AlertRuleThresholdType,
  11. AlertRuleTriggerType,
  12. Dataset,
  13. EventTypes,
  14. TargetType,
  15. Trigger,
  16. } from './types';
  17. export type PresetContext = {
  18. aggregate: string;
  19. comparisonType: AlertRuleComparisonType;
  20. dataset: Dataset;
  21. eventTypes: EventTypes[];
  22. name: string;
  23. thresholdType: AlertRuleThresholdType;
  24. triggers: Trigger[];
  25. comparisonDelta?: number;
  26. query?: string;
  27. timeWindow?: number;
  28. };
  29. export type Preset = {
  30. Icon: typeof IconGraph;
  31. // Will be shown on the corresponding alert type in the wizard.
  32. alertType: AlertType;
  33. description: string;
  34. id: string;
  35. makeContext(
  36. client: Client,
  37. project: Project,
  38. organization: Organization
  39. ): Promise<PresetContext>;
  40. makeUnqueriedContext(project: Project, organization: Organization): PresetContext;
  41. title: string;
  42. };
  43. async function getHighestVolumeTransaction(
  44. client: Client,
  45. organizationSlug: string,
  46. projectId: string
  47. ): Promise<[string, number] | null> {
  48. const result = await client.requestPromise(
  49. `/organizations/${organizationSlug}/events/`,
  50. {
  51. method: 'GET',
  52. data: {
  53. statsPeriod: '7d',
  54. project: projectId,
  55. field: ['count()', 'transaction'],
  56. sort: '-count',
  57. referrer: 'alert.presets.highest-volume',
  58. query: 'event.type:transaction',
  59. per_page: 1,
  60. },
  61. }
  62. );
  63. const transaction = result.data[0];
  64. if (transaction) {
  65. return [transaction.transaction, transaction['count()']];
  66. }
  67. return null;
  68. }
  69. function makeTeamCriticalAlert(project: Project, threshold: number = 200) {
  70. return {
  71. label: AlertRuleTriggerType.CRITICAL,
  72. alertThreshold: threshold,
  73. actions: project.teams.slice(0, 4).map(team => ({
  74. type: ActionType.EMAIL,
  75. targetType: TargetType.TEAM,
  76. targetIdentifier: team.id,
  77. unsavedDateCreated: new Date().toISOString(),
  78. inputChannelId: null,
  79. options: null,
  80. unsavedId: uniqueId(),
  81. })),
  82. };
  83. }
  84. function makeTeamWarningAlert(threshold: number = 100) {
  85. return {
  86. label: AlertRuleTriggerType.WARNING,
  87. alertThreshold: threshold,
  88. actions: [],
  89. };
  90. }
  91. export const PRESET_AGGREGATES: readonly Preset[] = [
  92. {
  93. id: 'p95-highest-volume',
  94. title: t('Slow transactions'),
  95. description: 'Get notified when important transactions are slower on average',
  96. Icon: IconGraph,
  97. alertType: 'trans_duration',
  98. makeUnqueriedContext(project, _) {
  99. return {
  100. name: t('p95 Alert for %s', [project.slug]),
  101. aggregate: 'p95(transaction.duration)',
  102. dataset: Dataset.TRANSACTIONS,
  103. eventTypes: [EventTypes.TRANSACTION],
  104. timeWindow: 60,
  105. comparisonDelta: 1440,
  106. comparisonType: AlertRuleComparisonType.CHANGE,
  107. thresholdType: AlertRuleThresholdType.ABOVE,
  108. triggers: [makeTeamCriticalAlert(project), makeTeamWarningAlert()],
  109. };
  110. },
  111. async makeContext(client, project, organization) {
  112. const transaction = (
  113. await getHighestVolumeTransaction(client, organization.slug, project.id)
  114. )?.[0];
  115. return {
  116. ...this.makeUnqueriedContext(project, organization),
  117. query: 'transaction:' + transaction,
  118. };
  119. },
  120. },
  121. {
  122. id: 'throughput-highest-volume',
  123. title: t('Throttled throughput'),
  124. description: 'Send an alert when transaction throughput drops significantly',
  125. Icon: IconGraph,
  126. alertType: 'throughput',
  127. makeUnqueriedContext(project, _) {
  128. return {
  129. name: t('Throughput Alert for %s', [project.slug]),
  130. aggregate: 'count()',
  131. dataset: Dataset.TRANSACTIONS,
  132. eventTypes: [EventTypes.TRANSACTION],
  133. timeWindow: 30,
  134. comparisonDelta: 24 * 60 * 7,
  135. comparisonType: AlertRuleComparisonType.CHANGE,
  136. thresholdType: AlertRuleThresholdType.BELOW,
  137. triggers: [makeTeamCriticalAlert(project, 500), makeTeamWarningAlert(300)],
  138. };
  139. },
  140. async makeContext(client, project, organization) {
  141. const transaction = (
  142. await getHighestVolumeTransaction(client, organization.slug, project.id)
  143. )?.[0];
  144. return {
  145. ...this.makeUnqueriedContext(project, organization),
  146. query: 'transaction:' + transaction,
  147. };
  148. },
  149. },
  150. {
  151. id: 'apdex-highest-volume',
  152. title: t('Apdex Score'),
  153. description:
  154. 'Learn when the ratio of satisfactory, tolerable, and frustrated requests drop',
  155. Icon: IconGraph,
  156. alertType: 'apdex',
  157. makeUnqueriedContext(project, _) {
  158. return {
  159. name: t('Apdex regression for %s', [project.slug]),
  160. aggregate: 'apdex(300)',
  161. dataset: Dataset.TRANSACTIONS,
  162. eventTypes: [EventTypes.TRANSACTION],
  163. timeWindow: 30,
  164. comparisonDelta: 24 * 60 * 7,
  165. comparisonType: AlertRuleComparisonType.CHANGE,
  166. thresholdType: AlertRuleThresholdType.BELOW,
  167. triggers: [makeTeamCriticalAlert(project), makeTeamWarningAlert()],
  168. };
  169. },
  170. async makeContext(client, project, organization) {
  171. const transaction = (
  172. await getHighestVolumeTransaction(client, organization.slug, project.id)
  173. )?.[0];
  174. return {
  175. ...this.makeUnqueriedContext(project, organization),
  176. query: 'transaction:' + transaction,
  177. };
  178. },
  179. },
  180. ] as const;