errors.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  127. obj[key] = aggregates[key];
  128. return obj;
  129. }, {}),
  130. fieldKeys: ERROR_FIELDS,
  131. });
  132. }
  133. export function getCustomEventsFieldRenderer(
  134. field: string,
  135. meta: MetaType,
  136. widget?: Widget
  137. ) {
  138. if (field === 'id') {
  139. return renderEventIdAsLinkable;
  140. }
  141. if (field === 'trace') {
  142. return renderTraceAsLinkable(widget);
  143. }
  144. return getFieldRenderer(field, meta, false);
  145. }
  146. export function getEventsRequest(
  147. api: Client,
  148. query: WidgetQuery,
  149. organization: Organization,
  150. pageFilters: PageFilters,
  151. limit?: number,
  152. cursor?: string,
  153. referrer?: string
  154. ) {
  155. const url = `/organizations/${organization.slug}/events/`;
  156. const eventView = eventViewFromWidget('', query, pageFilters);
  157. const params: DiscoverQueryRequestParams = {
  158. per_page: limit,
  159. cursor,
  160. referrer,
  161. dataset: DiscoverDatasets.ERRORS,
  162. };
  163. if (query.orderby) {
  164. params.sort = typeof query.orderby === 'string' ? [query.orderby] : query.orderby;
  165. }
  166. return doDiscoverQuery<EventsTableData>(
  167. api,
  168. url,
  169. {
  170. ...eventView.generateQueryStringObject(),
  171. ...params,
  172. },
  173. // Tries events request up to 3 times on rate limit
  174. {
  175. retry: {
  176. statusCodes: [429],
  177. tries: 3,
  178. },
  179. }
  180. );
  181. }
  182. // The y-axis options are a strict set of available aggregates
  183. export function filterYAxisOptions(_displayType: DisplayType) {
  184. return (option: FieldValueOption) => {
  185. return ERRORS_AGGREGATION_FUNCTIONS.includes(
  186. option.value.meta.name as AggregationKey
  187. );
  188. };
  189. }
  190. function getErrorsSeriesRequest(
  191. api: Client,
  192. widget: Widget,
  193. queryIndex: number,
  194. organization: Organization,
  195. pageFilters: PageFilters,
  196. _onDemandControlContext?: OnDemandControlContext,
  197. referrer?: string,
  198. _mepSetting?: MEPState | null
  199. ) {
  200. const requestData = getSeriesRequestData(
  201. widget,
  202. queryIndex,
  203. organization,
  204. pageFilters,
  205. DiscoverDatasets.ERRORS,
  206. referrer
  207. );
  208. return doEventsRequest<true>(api, requestData);
  209. }