presets.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import Link from 'app/components/links/link';
  2. import {t} from 'app/locale';
  3. import {Project} from 'app/types';
  4. import {DisplayModes} from 'app/utils/discover/types';
  5. import {tokenizeSearch} from 'app/utils/tokenizeSearch';
  6. import {Incident, IncidentStats} from 'app/views/alerts/types';
  7. import {getStartEndFromStats} from 'app/views/alerts/utils';
  8. import {getIncidentDiscoverUrl} from 'app/views/alerts/utils/getIncidentDiscoverUrl';
  9. import {transactionSummaryRouteWithQuery} from 'app/views/performance/transactionSummary/utils';
  10. import {Dataset} from './types';
  11. type PresetCta = {
  12. /**
  13. * The location to direct to upon clicking the CTA.
  14. */
  15. to: React.ComponentProps<typeof Link>['to'];
  16. /**
  17. * The CTA text
  18. */
  19. buttonText: string;
  20. /**
  21. * The tooltip title for the CTA button, may be empty.
  22. */
  23. title?: string;
  24. };
  25. type PresetCtaOpts = {
  26. orgSlug: string;
  27. projects: Project[];
  28. incident?: Incident;
  29. stats?: IncidentStats;
  30. };
  31. type Preset = {
  32. /**
  33. * The regex used to match aggregates to this preset.
  34. */
  35. match: RegExp;
  36. /**
  37. * The name of the preset
  38. */
  39. name: string;
  40. /**
  41. * The dataset that this preset applys to.
  42. */
  43. validDataset: Dataset[];
  44. /**
  45. * The default aggregate to use when selecting this preset
  46. */
  47. default: string;
  48. /**
  49. * Generates the CTA component
  50. */
  51. makeCtaParams: (opts: PresetCtaOpts) => PresetCta;
  52. };
  53. export const PRESET_AGGREGATES: Preset[] = [
  54. {
  55. name: t('Error count'),
  56. match: /^count\(\)/,
  57. validDataset: [Dataset.ERRORS],
  58. default: 'count()',
  59. /**
  60. * Simple "Open in Discover" button
  61. */
  62. makeCtaParams: makeDefaultCta,
  63. },
  64. {
  65. name: t('Users affected'),
  66. match: /^count_unique\(tags\[sentry:user\]\)/,
  67. validDataset: [Dataset.ERRORS],
  68. default: 'count_unique(tags[sentry:user])',
  69. /**
  70. * Simple "Open in Discover" button
  71. */
  72. makeCtaParams: makeDefaultCta,
  73. },
  74. {
  75. name: t('Latency'),
  76. match: /^(p[0-9]{2,3}|percentile\(transaction\.duration,[^)]+\)|avg\([^)]+\))/,
  77. validDataset: [Dataset.TRANSACTIONS],
  78. default: 'percentile(transaction.duration, 0.95)',
  79. /**
  80. * see: makeGenericTransactionCta
  81. */
  82. makeCtaParams: opts =>
  83. makeGenericTransactionCta({
  84. opts,
  85. tooltip: t('Latency by Transaction'),
  86. }),
  87. },
  88. {
  89. name: t('Apdex'),
  90. match: /^apdex\([0-9.]+\)/,
  91. validDataset: [Dataset.TRANSACTIONS],
  92. default: 'apdex(300)',
  93. /**
  94. * see: makeGenericTransactionCta
  95. */
  96. makeCtaParams: opts =>
  97. makeGenericTransactionCta({
  98. opts,
  99. tooltip: t('Apdex by Transaction'),
  100. }),
  101. },
  102. {
  103. name: t('Transaction Count'),
  104. match: /^count\(\)/,
  105. validDataset: [Dataset.TRANSACTIONS],
  106. default: 'count()',
  107. /**
  108. * see: makeGenericTransactionCta
  109. */
  110. makeCtaParams: opts => makeGenericTransactionCta({opts}),
  111. },
  112. {
  113. name: t('Failure rate'),
  114. match: /^failure_rate\(\)/,
  115. validDataset: [Dataset.TRANSACTIONS],
  116. default: 'failure_rate()',
  117. /**
  118. * See makeFailureRateCta
  119. */
  120. makeCtaParams: makeFailureRateCta,
  121. },
  122. ];
  123. /**
  124. * - CASE 1: If has a specific transaction filter
  125. * - CTA is: "View Transaction Summary"
  126. * - Tooltip is the transaction name
  127. * - the same period as the alert graph (i.e. with alert start time in the middle)
  128. *
  129. * - CASE 2: If transaction is NOT filtered, or has a * filter:
  130. * - "Open in Discover" button with optional tooltip which opens a discover view with...
  131. * - fields {transaction, count(), <metric>} sorted by count()
  132. * - top-5 activated
  133. */
  134. function makeGenericTransactionCta(opts: {
  135. opts: PresetCtaOpts;
  136. tooltip?: string;
  137. }): PresetCta {
  138. const {
  139. opts: {orgSlug, projects, incident, stats},
  140. tooltip,
  141. } = opts;
  142. if (!incident || !stats) {
  143. return {to: '', buttonText: t('Incident details')};
  144. }
  145. const query = tokenizeSearch(incident.discoverQuery ?? '');
  146. const transaction = query
  147. .getTagValues('transaction')
  148. ?.find(filter => !filter.includes('*'));
  149. // CASE 1
  150. if (transaction !== undefined) {
  151. const period = getStartEndFromStats(stats);
  152. const summaryUrl = transactionSummaryRouteWithQuery({
  153. orgSlug,
  154. transaction,
  155. projectID: projects
  156. .filter(({slug}) => incident.projects.includes(slug))
  157. .map(({id}) => id),
  158. query: {...period},
  159. });
  160. return {
  161. to: summaryUrl,
  162. buttonText: t('View Transaction Summary'),
  163. title: transaction,
  164. };
  165. }
  166. // CASE 2
  167. const extraQueryParams = {
  168. fields: [...new Set(['transaction', 'count()', incident.alertRule.aggregate])],
  169. orderby: '-count',
  170. display: DisplayModes.TOP5,
  171. };
  172. const discoverUrl = getIncidentDiscoverUrl({
  173. orgSlug,
  174. projects,
  175. incident,
  176. stats,
  177. extraQueryParams,
  178. });
  179. return {
  180. to: discoverUrl,
  181. buttonText: t('Open in Discover'),
  182. title: tooltip,
  183. };
  184. }
  185. /**
  186. * - CASE 1: Filtered to a specific transaction, "Open in Discover" with...
  187. * - fields [transaction.status, count()] sorted by count(),
  188. * - "Top 5 period" activated.
  189. *
  190. * - CASE 2: If filtered on multiple transactions, "Open in Discover" button
  191. * with tooltip "Failure rate by transaction" which opens a discover view
  192. * - fields [transaction, failure_rate()] sorted by failure_rate
  193. * - top 5 activated
  194. */
  195. function makeFailureRateCta({orgSlug, incident, projects, stats}: PresetCtaOpts) {
  196. if (!incident || !stats) {
  197. return {to: '', buttonText: t('Incident details')};
  198. }
  199. const query = tokenizeSearch(incident.discoverQuery ?? '');
  200. const transaction = query
  201. .getTagValues('transaction')
  202. ?.find(filter => !filter.includes('*'));
  203. const extraQueryParams =
  204. transaction !== undefined
  205. ? // CASE 1
  206. {
  207. fields: ['transaction.status', 'count()'],
  208. orderby: '-count',
  209. display: DisplayModes.TOP5,
  210. }
  211. : // Case 2
  212. {
  213. fields: ['transaction', 'failure_rate()'],
  214. orderby: '-failure_rate',
  215. display: DisplayModes.TOP5,
  216. };
  217. const discoverUrl = getIncidentDiscoverUrl({
  218. orgSlug,
  219. projects,
  220. incident,
  221. stats,
  222. extraQueryParams,
  223. });
  224. return {
  225. to: discoverUrl,
  226. buttonText: t('Open in Discover'),
  227. title: transaction === undefined ? t('Failure rate by transaction') : undefined,
  228. };
  229. }
  230. /**
  231. * Get the CTA used for alerts that do not have a preset
  232. */
  233. export function makeDefaultCta({
  234. orgSlug,
  235. projects,
  236. incident,
  237. stats,
  238. }: PresetCtaOpts): PresetCta {
  239. if (!incident) {
  240. return {
  241. buttonText: t('Open in Discover'),
  242. to: '',
  243. };
  244. }
  245. const extraQueryParams = {
  246. display: DisplayModes.TOP5,
  247. };
  248. return {
  249. buttonText: t('Open in Discover'),
  250. to: getIncidentDiscoverUrl({orgSlug, projects, incident, stats, extraQueryParams}),
  251. };
  252. }