data.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import {t} from 'sentry/locale';
  2. import ConfigStore from 'sentry/stores/configStore';
  3. import type {Organization} from 'sentry/types/organization';
  4. import {TOP_N} from 'sentry/utils/discover/types';
  5. import {hasDatasetSelector} from 'sentry/views/dashboards/utils';
  6. import type {Widget} from '../types';
  7. import {DisplayType, WidgetType} from '../types';
  8. export type WidgetTemplate = Widget & {
  9. description: string;
  10. };
  11. export const getDefaultWidgets = (organization: Organization) => {
  12. const isSelfHostedErrorsOnly = ConfigStore.get('isSelfHostedErrorsOnly');
  13. const transactionsWidgets = [
  14. {
  15. id: 'duration-distribution',
  16. title: t('Duration Distribution'),
  17. description: t('Compare transaction durations across different percentiles.'),
  18. displayType: DisplayType.LINE,
  19. widgetType: organization.features.includes('performance-discover-dataset-selector')
  20. ? WidgetType.TRANSACTIONS
  21. : WidgetType.DISCOVER,
  22. interval: '5m',
  23. queries: [
  24. {
  25. name: '',
  26. conditions: hasDatasetSelector(organization) ? '' : 'event.type:transaction',
  27. fields: [
  28. 'p50(transaction.duration)',
  29. 'p75(transaction.duration)',
  30. 'p95(transaction.duration)',
  31. ],
  32. aggregates: [
  33. 'p50(transaction.duration)',
  34. 'p75(transaction.duration)',
  35. 'p95(transaction.duration)',
  36. ],
  37. columns: [],
  38. orderby: '',
  39. },
  40. ],
  41. },
  42. {
  43. id: 'high-throughput-transactions',
  44. title: t('High Throughput Transactions'),
  45. description: t('Top 5 transactions with the largest volume.'),
  46. displayType: DisplayType.TOP_N,
  47. widgetType: organization.features.includes('performance-discover-dataset-selector')
  48. ? WidgetType.TRANSACTIONS
  49. : WidgetType.DISCOVER,
  50. interval: '5m',
  51. queries: [
  52. {
  53. name: '',
  54. conditions: hasDatasetSelector(organization) ? '' : 'event.type:transaction',
  55. fields: ['transaction', 'count()'],
  56. aggregates: ['count()'],
  57. columns: ['transaction'],
  58. orderby: '-count()',
  59. },
  60. ],
  61. },
  62. {
  63. id: 'crash-rates-recent-releases',
  64. title: t('Crash Rates for Recent Releases'),
  65. description: t('Percentage of crashed sessions for latest releases.'),
  66. displayType: DisplayType.LINE,
  67. widgetType: WidgetType.RELEASE,
  68. interval: '5m',
  69. limit: 8,
  70. queries: [
  71. {
  72. name: '',
  73. conditions: '',
  74. fields: ['crash_rate(session)', 'release'],
  75. aggregates: ['crash_rate(session)'],
  76. columns: ['release'],
  77. orderby: '-release',
  78. },
  79. ],
  80. },
  81. {
  82. id: 'session-health',
  83. title: t('Session Health'),
  84. description: t('Number of abnormal, crashed, errored and healthy sessions.'),
  85. displayType: DisplayType.TABLE,
  86. widgetType: WidgetType.RELEASE,
  87. interval: '5m',
  88. queries: [
  89. {
  90. name: '',
  91. conditions: '',
  92. fields: ['session.status', 'sum(session)'],
  93. aggregates: ['sum(session)'],
  94. columns: ['session.status'],
  95. orderby: '-sum(session)',
  96. },
  97. ],
  98. },
  99. {
  100. id: 'lcp-country',
  101. title: t('LCP by Country'),
  102. description: t('Table showing page load times by country.'),
  103. displayType: DisplayType.TABLE,
  104. widgetType: organization.features.includes('performance-discover-dataset-selector')
  105. ? WidgetType.TRANSACTIONS
  106. : WidgetType.DISCOVER,
  107. interval: '5m',
  108. queries: [
  109. {
  110. name: '',
  111. conditions: 'has:geo.country_code',
  112. fields: ['geo.country_code', 'geo.region', 'p75(measurements.lcp)'],
  113. aggregates: ['p75(measurements.lcp)'],
  114. columns: ['geo.country_code', 'geo.region'],
  115. orderby: '-p75(measurements.lcp)',
  116. },
  117. ],
  118. },
  119. {
  120. id: 'miserable-users',
  121. title: t('Miserable Users'),
  122. description: t('Unique users who have experienced slow load times.'),
  123. displayType: DisplayType.BIG_NUMBER,
  124. widgetType: organization.features.includes('performance-discover-dataset-selector')
  125. ? WidgetType.TRANSACTIONS
  126. : WidgetType.DISCOVER,
  127. interval: '5m',
  128. queries: [
  129. {
  130. name: '',
  131. conditions: '',
  132. fields: ['count_miserable(user,300)'],
  133. aggregates: ['count_miserable(user,300)'],
  134. columns: [],
  135. orderby: '',
  136. },
  137. ],
  138. },
  139. {
  140. id: 'slow-vs-fast',
  141. title: t('Slow vs. Fast Transactions'),
  142. description: t(
  143. 'Percentage breakdown of transaction durations over and under 300ms.'
  144. ),
  145. displayType: DisplayType.BAR,
  146. widgetType: organization.features.includes('performance-discover-dataset-selector')
  147. ? WidgetType.TRANSACTIONS
  148. : WidgetType.DISCOVER,
  149. interval: '5m',
  150. queries: [
  151. {
  152. name: '',
  153. conditions: hasDatasetSelector(organization) ? '' : 'event.type:transaction',
  154. fields: [
  155. 'equation|(count_if(transaction.duration,greater,300) / count()) * 100',
  156. 'equation|(count_if(transaction.duration,lessOrEquals,300) / count()) * 100',
  157. ],
  158. aggregates: [
  159. 'equation|(count_if(transaction.duration,greater,300) / count()) * 100',
  160. 'equation|(count_if(transaction.duration,lessOrEquals,300) / count()) * 100',
  161. ],
  162. columns: [],
  163. orderby: '',
  164. },
  165. ],
  166. },
  167. ];
  168. const errorsWidgets = [
  169. {
  170. id: 'issue-for-review',
  171. title: t('Issues For Review'),
  172. description: t('Most recently seen unresolved issues for review.'),
  173. displayType: DisplayType.TABLE,
  174. widgetType: WidgetType.ISSUE,
  175. interval: '5m',
  176. queries: [
  177. {
  178. name: '',
  179. conditions: 'is:unresolved is:for_review',
  180. fields: ['issue', 'assignee', 'events', 'title'],
  181. aggregates: [],
  182. columns: ['issue', 'assignee', 'events', 'title'],
  183. orderby: 'date',
  184. },
  185. ],
  186. },
  187. {
  188. id: 'top-unhandled',
  189. title: t('Top Unhandled Error Types'),
  190. description: t('Most frequently encountered unhandled errors.'),
  191. displayType: DisplayType.TOP_N,
  192. widgetType: organization.features.includes('performance-discover-dataset-selector')
  193. ? WidgetType.ERRORS
  194. : WidgetType.DISCOVER,
  195. interval: '5m',
  196. queries: [
  197. {
  198. name: '',
  199. conditions: 'error.unhandled:true',
  200. fields: ['error.type', 'count()'],
  201. aggregates: ['count()'],
  202. columns: ['error.type'],
  203. orderby: '-count()',
  204. },
  205. ],
  206. },
  207. {
  208. id: 'users-affected',
  209. title: t('Users Affected by Errors'),
  210. description: t('Footprint of unique users affected by errors.'),
  211. displayType: DisplayType.LINE,
  212. widgetType: organization.features.includes('performance-discover-dataset-selector')
  213. ? WidgetType.ERRORS
  214. : WidgetType.DISCOVER,
  215. interval: '5m',
  216. queries: [
  217. {
  218. name: '',
  219. conditions: hasDatasetSelector(organization) ? '' : 'event.type:error',
  220. fields: ['count_unique(user)', 'count()'],
  221. aggregates: ['count_unique(user)', 'count()'],
  222. columns: [],
  223. orderby: '',
  224. },
  225. ],
  226. },
  227. ];
  228. return isSelfHostedErrorsOnly
  229. ? errorsWidgets
  230. : [...transactionsWidgets, ...errorsWidgets];
  231. };
  232. export function getTopNConvertedDefaultWidgets(
  233. organization: Organization
  234. ): Readonly<Array<WidgetTemplate>> {
  235. return getDefaultWidgets(organization).map(widget => {
  236. if (widget.displayType === DisplayType.TOP_N) {
  237. return {
  238. ...widget,
  239. displayType: DisplayType.AREA,
  240. limit: TOP_N,
  241. };
  242. }
  243. return widget;
  244. });
  245. }