import {css} from '@emotion/react'; import styled from '@emotion/styled'; import {PlatformIcon} from 'platformicons'; import ProjectBadge from 'sentry/components/idBadge/projectBadge'; import {Tooltip} from 'sentry/components/tooltip'; import {CHART_PALETTE} from 'sentry/constants/chartPalette'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {formatAbbreviatedNumber} from 'sentry/utils/formatters'; import {formatNumberWithDynamicDecimalPoints} from 'sentry/utils/number/formatNumberWithDynamicDecimalPoints'; import type {ProjectSampleCount} from 'sentry/views/settings/dynamicSampling/utils/useProjectSampleCounts'; const ITEMS_TO_SHOW = 5; const palette = CHART_PALETTE[ITEMS_TO_SHOW - 1]; interface Props extends React.HTMLAttributes { sampleCounts: ProjectSampleCount[]; sampleRates: Record; } function OthersBadge() { return (
{t('other projects')}
); } export function SamplingBreakdown({sampleCounts, sampleRates, ...props}: Props) { const spansWithSampleRates = sampleCounts ?.map(item => { const sampledSpans = Math.floor(item.count * (sampleRates[item.project.id] ?? 1)); return { project: item.project, sampledSpans, }; }) .toSorted((a, b) => b.sampledSpans - a.sampledSpans); const hasOthers = spansWithSampleRates.length > ITEMS_TO_SHOW; const topItems = hasOthers ? spansWithSampleRates.slice(0, ITEMS_TO_SHOW - 1) : spansWithSampleRates.slice(0, ITEMS_TO_SHOW); const otherSpanCount = spansWithSampleRates .slice(ITEMS_TO_SHOW - 1) .reduce((acc, item) => acc + item.sampledSpans, 0); const total = spansWithSampleRates.reduce((acc, item) => acc + item.sampledSpans, 0); const getSpanPercent = spanCount => (total === 0 ? 100 : (spanCount / total) * 100); const otherPercent = getSpanPercent(otherSpanCount); return (
{t('Breakdown of stored spans')} {t('Total: %s', formatAbbreviatedNumber(total))} {topItems.map((item, index) => { return ( {`${formatNumberWithDynamicDecimalPoints(getSpanPercent(item.sampledSpans))}%`} {formatAbbreviatedNumber(item.sampledSpans)} } skipWrapper >
); })} {hasOthers && ( {`${formatNumberWithDynamicDecimalPoints(otherPercent)}%`} {formatAbbreviatedNumber(total)} } skipWrapper >
)} {topItems.map(item => { return ( {`${formatNumberWithDynamicDecimalPoints(getSpanPercent(item.sampledSpans))}%`} ); })} {hasOthers && ( {`${formatNumberWithDynamicDecimalPoints(otherPercent)}%`} )}
); } const Heading = styled('h6')` margin-bottom: ${space(1)}; font-size: ${p => p.theme.fontSizeMedium}; display: flex; justify-content: space-between; `; const Breakdown = styled('div')` display: flex; height: ${space(2)}; width: 100%; border-radius: ${p => p.theme.borderRadius}; overflow: hidden; `; const Legend = styled('div')` display: flex; flex-wrap: wrap; margin-top: ${space(1)}; gap: ${space(1.5)}; `; const LegendItem = styled('div')` display: flex; align-items: center; gap: ${space(0.75)}; `; const SubText = styled('span')` color: ${p => p.theme.gray300}; white-space: nowrap; `;