errors.tsx 6.7 KB

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