useSpanSamples.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import moment from 'moment-timezone';
  2. import * as qs from 'query-string';
  3. import {useQuery} from 'sentry/utils/queryClient';
  4. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  5. import useApi from 'sentry/utils/useApi';
  6. import {useLocation} from 'sentry/utils/useLocation';
  7. import useOrganization from 'sentry/utils/useOrganization';
  8. import usePageFilters from 'sentry/utils/usePageFilters';
  9. import {computeAxisMax} from 'sentry/views/insights/common/components/chart';
  10. import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
  11. import {DATE_FORMAT} from 'sentry/views/insights/common/queries/useSpansQuery';
  12. import {getDateConditions} from 'sentry/views/insights/common/utils/getDateConditions';
  13. import type {
  14. SpanIndexedFieldTypes,
  15. SpanMetricsQueryFilters,
  16. SubregionCode,
  17. } from 'sentry/views/insights/types';
  18. import {SpanIndexedField, SpanMetricsField} from 'sentry/views/insights/types';
  19. const {SPAN_SELF_TIME, SPAN_GROUP} = SpanIndexedField;
  20. type Options = {
  21. groupId: string;
  22. transactionName: string;
  23. additionalFields?: string[];
  24. release?: string;
  25. spanSearch?: MutableSearch;
  26. subregions?: SubregionCode[];
  27. transactionMethod?: string;
  28. };
  29. export type SpanSample = Pick<
  30. SpanIndexedFieldTypes,
  31. | SpanIndexedField.SPAN_SELF_TIME
  32. | SpanIndexedField.TRANSACTION_ID
  33. | SpanIndexedField.PROJECT
  34. | SpanIndexedField.TIMESTAMP
  35. | SpanIndexedField.ID
  36. | SpanIndexedField.PROFILE_ID
  37. | SpanIndexedField.HTTP_RESPONSE_CONTENT_LENGTH
  38. | SpanIndexedField.TRACE
  39. >;
  40. export const useSpanSamples = (options: Options) => {
  41. const organization = useOrganization();
  42. const url = `/api/0/organizations/${organization.slug}/spans-samples/`;
  43. const api = useApi();
  44. const pageFilter = usePageFilters();
  45. const {
  46. groupId,
  47. transactionName,
  48. transactionMethod,
  49. release,
  50. spanSearch,
  51. additionalFields,
  52. subregions,
  53. } = options;
  54. const location = useLocation();
  55. const query = spanSearch !== undefined ? spanSearch.copy() : new MutableSearch([]);
  56. query.addFilterValue(SPAN_GROUP, groupId);
  57. query.addFilterValue('transaction', transactionName);
  58. const filters: SpanMetricsQueryFilters = {
  59. transaction: transactionName,
  60. };
  61. if (transactionMethod) {
  62. query.addFilterValue('transaction.method', transactionMethod);
  63. filters['transaction.method'] = transactionMethod;
  64. }
  65. if (release) {
  66. query.addFilterValue('release', release);
  67. filters.release = release;
  68. }
  69. if (subregions) {
  70. query.addDisjunctionFilterValues(SpanMetricsField.USER_GEO_SUBREGION, subregions);
  71. filters[SpanMetricsField.USER_GEO_SUBREGION] = `[${subregions.join(',')}]`;
  72. }
  73. const dateCondtions = getDateConditions(pageFilter.selection);
  74. const {isPending: isLoadingSeries, data: spanMetricsSeriesData} = useSpanMetricsSeries(
  75. {
  76. search: MutableSearch.fromQueryObject({'span.group': groupId, ...filters}),
  77. yAxis: [`avg(${SPAN_SELF_TIME})`],
  78. enabled: Object.values({'span.group': groupId, ...filters}).every(value =>
  79. Boolean(value)
  80. ),
  81. },
  82. 'api.starfish.sidebar-span-metrics'
  83. );
  84. const maxYValue = computeAxisMax([spanMetricsSeriesData?.[`avg(${SPAN_SELF_TIME})`]]);
  85. const enabled = Boolean(
  86. groupId && transactionName && !isLoadingSeries && pageFilter.isReady
  87. );
  88. const queryString = query.formatString();
  89. const result = useQuery<SpanSample[]>({
  90. queryKey: [
  91. 'span-samples',
  92. groupId,
  93. transactionName,
  94. dateCondtions.statsPeriod,
  95. dateCondtions.start,
  96. dateCondtions.end,
  97. queryString,
  98. subregions?.join(','),
  99. additionalFields?.join(','),
  100. ],
  101. queryFn: async () => {
  102. const {data} = await api.requestPromise(
  103. `${url}?${qs.stringify({
  104. ...dateCondtions,
  105. ...{utc: location.query.utc},
  106. lowerBound: 0,
  107. firstBound: maxYValue * (1 / 3),
  108. secondBound: maxYValue * (2 / 3),
  109. upperBound: maxYValue,
  110. project: pageFilter.selection.projects,
  111. query: queryString,
  112. ...(additionalFields?.length ? {additionalFields} : {}),
  113. })}`
  114. );
  115. return data
  116. ?.map((d: SpanSample) => ({
  117. ...d,
  118. timestamp: moment(d.timestamp).format(DATE_FORMAT),
  119. }))
  120. .sort((a: SpanSample, b: SpanSample) => b[SPAN_SELF_TIME] - a[SPAN_SELF_TIME]);
  121. },
  122. refetchOnWindowFocus: false,
  123. enabled,
  124. initialData: [],
  125. });
  126. return {...result, isEnabled: enabled};
  127. };