traceLevelOpsBreakdown.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import styled from '@emotion/styled';
  2. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  3. import {pickBarColor} from 'sentry/components/performance/waterfall/utils';
  4. import Placeholder from 'sentry/components/placeholder';
  5. import {IconCircleFill} from 'sentry/icons/iconCircleFill';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import type {NewQuery} from 'sentry/types/organization';
  9. import {useDiscoverQuery} from 'sentry/utils/discover/discoverQuery';
  10. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  11. import type {Color} from 'sentry/utils/theme';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {useTraceEventView} from './useTraceEventView';
  15. import {type TraceViewQueryParams, useTraceQueryParams} from './useTraceQueryParams';
  16. function useTraceLevelOpsQuery(
  17. traceSlug: string,
  18. params: TraceViewQueryParams,
  19. partialSavedQuery: Partial<NewQuery>
  20. ) {
  21. const location = useLocation();
  22. const organization = useOrganization();
  23. const eventView = useTraceEventView(traceSlug, params, {
  24. ...partialSavedQuery,
  25. dataset: DiscoverDatasets.SPANS_EAP,
  26. });
  27. return useDiscoverQuery({
  28. eventView,
  29. orgSlug: organization.slug,
  30. location,
  31. });
  32. }
  33. function LoadingPlaceHolder() {
  34. return (
  35. <Container>
  36. <StyledPlaceholder height={'16px'} width={'100px'} />
  37. <StyledPlaceholder height={'16px'} width={'100px'} />
  38. <StyledPlaceholder height={'16px'} width={'100px'} />
  39. </Container>
  40. );
  41. }
  42. type Props = {
  43. isTraceLoading: boolean;
  44. traceSlug: string;
  45. };
  46. export function TraceLevelOpsBreakdown({traceSlug, isTraceLoading}: Props) {
  47. const urlParams = useTraceQueryParams();
  48. const {
  49. data: opsCountsResult,
  50. isPending: isOpsCountsLoading,
  51. isError: isOpsCountsError,
  52. } = useTraceLevelOpsQuery(traceSlug ?? '', urlParams, {
  53. fields: ['span.op', 'count()'],
  54. orderby: '-count',
  55. });
  56. const {
  57. data: totalCountResult,
  58. isPending: isTotalCountLoading,
  59. isError: isTotalCountError,
  60. } = useTraceLevelOpsQuery(traceSlug ?? '', urlParams, {
  61. fields: ['count()'],
  62. });
  63. if (isOpsCountsLoading || isTotalCountLoading || isTraceLoading) {
  64. return <LoadingPlaceHolder />;
  65. }
  66. if (isOpsCountsError || isTotalCountError) {
  67. addErrorMessage(t('Failed to load trace level ops breakdown'));
  68. return null;
  69. }
  70. const totalCount = totalCountResult?.data[0]?.['count()'] ?? 0;
  71. if (typeof totalCount !== 'number' || totalCount <= 0) {
  72. return null;
  73. }
  74. return (
  75. <Container>
  76. {opsCountsResult?.data.slice(0, 4).map(currOp => {
  77. const operationName = currOp['span.op'];
  78. const count = currOp['count()'];
  79. if (typeof operationName !== 'string' || typeof count !== 'number') {
  80. return null;
  81. }
  82. const percentage = count / totalCount;
  83. const color = pickBarColor(operationName);
  84. const pctLabel = isFinite(percentage) ? Math.round(percentage * 100) : '∞';
  85. return (
  86. <HighlightsOpRow key={operationName}>
  87. <IconCircleFill size="xs" color={color as Color} />
  88. {operationName}
  89. <span>{pctLabel}%</span>
  90. </HighlightsOpRow>
  91. );
  92. })}
  93. </Container>
  94. );
  95. }
  96. const HighlightsOpRow = styled('div')`
  97. display: flex;
  98. align-items: center;
  99. font-size: ${p => p.theme.fontSizeSmall};
  100. gap: 5px;
  101. `;
  102. const Container = styled('div')`
  103. display: flex;
  104. align-items: center;
  105. padding-left: ${space(1)};
  106. gap: ${space(2)};
  107. `;
  108. const StyledPlaceholder = styled(Placeholder)`
  109. border-radius: ${p => p.theme.borderRadius};
  110. `;