errors.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import {doEventsRequest} from 'sentry/actionCreators/events';
  2. import type {Client} from 'sentry/api';
  3. import type {PageFilters} from 'sentry/types/core';
  4. import type {Series} from 'sentry/types/echarts';
  5. import type {TagCollection} from 'sentry/types/group';
  6. import type {
  7. EventsStats,
  8. MultiSeriesEventsStats,
  9. Organization,
  10. } from 'sentry/types/organization';
  11. import type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
  12. import type {EventsTableData, TableData} from 'sentry/utils/discover/discoverQuery';
  13. import type {MetaType} from 'sentry/utils/discover/eventView';
  14. import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
  15. import {
  16. ERROR_FIELDS,
  17. ERRORS_AGGREGATION_FUNCTIONS,
  18. getAggregations,
  19. type QueryFieldValue,
  20. } from 'sentry/utils/discover/fields';
  21. import type {DiscoverQueryRequestParams} from 'sentry/utils/discover/genericDiscoverQuery';
  22. import {doDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
  23. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  24. import type {AggregationKey} from 'sentry/utils/fields';
  25. import type {MEPState} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  26. import type {OnDemandControlContext} from 'sentry/utils/performance/contexts/onDemandControl';
  27. import {getSeriesRequestData} from 'sentry/views/dashboards/datasetConfig/utils/getSeriesRequestData';
  28. import type {FieldValueOption} from 'sentry/views/discover/table/queryField';
  29. import {FieldValueKind} from 'sentry/views/discover/table/types';
  30. import {generateFieldOptions} from 'sentry/views/discover/utils';
  31. import type {Widget, WidgetQuery} from '../types';
  32. import {DisplayType} from '../types';
  33. import {eventViewFromWidget} from '../utils';
  34. import {EventsSearchBar} from '../widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar';
  35. import {type DatasetConfig, handleOrderByReset} from './base';
  36. import {
  37. filterAggregateParams,
  38. filterSeriesSortOptions,
  39. filterYAxisAggregateParams, // TODO: Does this need to be overridden?
  40. getTableSortOptions,
  41. getTimeseriesSortOptions,
  42. renderEventIdAsLinkable,
  43. renderTraceAsLinkable,
  44. transformEventsResponseToSeries,
  45. transformEventsResponseToTable,
  46. } from './errorsAndTransactions';
  47. const DEFAULT_WIDGET_QUERY: WidgetQuery = {
  48. name: '',
  49. fields: ['count()'],
  50. columns: [],
  51. fieldAliases: [],
  52. aggregates: ['count()'],
  53. conditions: '',
  54. orderby: '-count()',
  55. };
  56. const DEFAULT_FIELD: QueryFieldValue = {
  57. function: ['count', '', undefined, undefined],
  58. kind: FieldValueKind.FUNCTION,
  59. };
  60. export type SeriesWithOrdering = [order: number, series: Series];
  61. export const ErrorsConfig: DatasetConfig<
  62. EventsStats | MultiSeriesEventsStats,
  63. TableData | EventsTableData
  64. > = {
  65. defaultField: DEFAULT_FIELD,
  66. defaultWidgetQuery: DEFAULT_WIDGET_QUERY,
  67. enableEquations: true,
  68. getCustomFieldRenderer: getCustomEventsFieldRenderer,
  69. SearchBar: EventsSearchBar,
  70. filterSeriesSortOptions,
  71. filterYAxisAggregateParams,
  72. filterYAxisOptions,
  73. getTableFieldOptions: getEventsTableFieldOptions,
  74. getTimeseriesSortOptions: (organization, widgetQuery, tags) =>
  75. getTimeseriesSortOptions(organization, widgetQuery, tags, getEventsTableFieldOptions),
  76. getTableSortOptions,
  77. getGroupByFieldOptions: getEventsTableFieldOptions,
  78. handleOrderByReset,
  79. supportedDisplayTypes: [
  80. DisplayType.AREA,
  81. DisplayType.BAR,
  82. DisplayType.BIG_NUMBER,
  83. DisplayType.LINE,
  84. DisplayType.TABLE,
  85. DisplayType.TOP_N,
  86. ],
  87. getTableRequest: (
  88. api: Client,
  89. _widget: Widget,
  90. query: WidgetQuery,
  91. organization: Organization,
  92. pageFilters: PageFilters,
  93. _onDemandControlContext?: OnDemandControlContext,
  94. limit?: number,
  95. cursor?: string,
  96. referrer?: string,
  97. _mepSetting?: MEPState | null
  98. ) => {
  99. return getEventsRequest(
  100. api,
  101. query,
  102. organization,
  103. pageFilters,
  104. limit,
  105. cursor,
  106. referrer
  107. );
  108. },
  109. getSeriesRequest: getErrorsSeriesRequest,
  110. transformTable: transformEventsResponseToTable,
  111. transformSeries: transformEventsResponseToSeries,
  112. filterAggregateParams,
  113. };
  114. function getEventsTableFieldOptions(
  115. organization: Organization,
  116. tags?: TagCollection,
  117. _customMeasurements?: CustomMeasurementCollection
  118. ) {
  119. const aggregates = getAggregations(DiscoverDatasets.ERRORS);
  120. return generateFieldOptions({
  121. organization,
  122. tagKeys: Object.values(tags ?? {}).map(({key}) => key),
  123. aggregations: Object.keys(aggregates)
  124. .filter(key => ERRORS_AGGREGATION_FUNCTIONS.includes(key as AggregationKey))
  125. .reduce((obj, key) => {
  126. obj[key] = aggregates[key];
  127. return obj;
  128. }, {}),
  129. fieldKeys: ERROR_FIELDS,
  130. });
  131. }
  132. export function getCustomEventsFieldRenderer(field: string, meta: MetaType) {
  133. if (field === 'id') {
  134. return renderEventIdAsLinkable;
  135. }
  136. if (field === 'trace') {
  137. return renderTraceAsLinkable;
  138. }
  139. return getFieldRenderer(field, meta, false);
  140. }
  141. export function getEventsRequest(
  142. api: Client,
  143. query: WidgetQuery,
  144. organization: Organization,
  145. pageFilters: PageFilters,
  146. limit?: number,
  147. cursor?: string,
  148. referrer?: string
  149. ) {
  150. const url = `/organizations/${organization.slug}/events/`;
  151. const eventView = eventViewFromWidget('', query, pageFilters);
  152. const params: DiscoverQueryRequestParams = {
  153. per_page: limit,
  154. cursor,
  155. referrer,
  156. dataset: DiscoverDatasets.ERRORS,
  157. };
  158. if (query.orderby) {
  159. params.sort = typeof query.orderby === 'string' ? [query.orderby] : query.orderby;
  160. }
  161. return doDiscoverQuery<EventsTableData>(
  162. api,
  163. url,
  164. {
  165. ...eventView.generateQueryStringObject(),
  166. ...params,
  167. },
  168. // Tries events request up to 3 times on rate limit
  169. {
  170. retry: {
  171. statusCodes: [429],
  172. tries: 3,
  173. },
  174. }
  175. );
  176. }
  177. // The y-axis options are a strict set of available aggregates
  178. export function filterYAxisOptions(_displayType: DisplayType) {
  179. return (option: FieldValueOption) => {
  180. return ERRORS_AGGREGATION_FUNCTIONS.includes(
  181. option.value.meta.name as AggregationKey
  182. );
  183. };
  184. }
  185. function getErrorsSeriesRequest(
  186. api: Client,
  187. widget: Widget,
  188. queryIndex: number,
  189. organization: Organization,
  190. pageFilters: PageFilters,
  191. _onDemandControlContext?: OnDemandControlContext,
  192. referrer?: string,
  193. _mepSetting?: MEPState | null
  194. ) {
  195. const requestData = getSeriesRequestData(
  196. widget,
  197. queryIndex,
  198. organization,
  199. pageFilters,
  200. DiscoverDatasets.ERRORS,
  201. referrer
  202. );
  203. return doEventsRequest<true>(api, requestData);
  204. }