diff --git a/static/app/views/alerts/rules/metric/utils/determineSeriesSampleCount.tsx b/static/app/views/alerts/rules/metric/utils/determineSeriesSampleCount.tsx
index 5d9401391f1308..0bae87c0e287dd 100644
--- a/static/app/views/alerts/rules/metric/utils/determineSeriesSampleCount.tsx
+++ b/static/app/views/alerts/rules/metric/utils/determineSeriesSampleCount.tsx
@@ -44,7 +44,7 @@ export function determineSeriesSampleCountAndIsSampled(
}
const sampleRate = data[i]!.values[j]!.sampleRate;
- if (sampleRate === 1) {
+ if (defined(sampleRate) && sampleRate >= 1) {
hasUnsampledInterval = true;
} else if (defined(sampleRate) && sampleRate < 1) {
hasSampledInterval = true;
diff --git a/static/app/views/explore/logs/confidenceFooter.spec.tsx b/static/app/views/explore/logs/confidenceFooter.spec.tsx
new file mode 100644
index 00000000000000..0226941ad6fe11
--- /dev/null
+++ b/static/app/views/explore/logs/confidenceFooter.spec.tsx
@@ -0,0 +1,582 @@
+import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
+
+import type {ChartInfo} from 'sentry/views/explore/components/chart/types';
+import {ChartType} from 'sentry/views/insights/common/components/chart';
+
+import {ConfidenceFooter} from './confidenceFooter';
+
+function Wrapper({children}: {children: React.ReactNode}) {
+ return
{children}
;
+}
+
+describe('ConfidenceFooter', () => {
+ const rawLogCounts = {
+ normal: {
+ count: 100,
+ isLoading: false,
+ },
+ highAccuracy: {
+ count: 1000,
+ isLoading: false,
+ },
+ };
+
+ function chartInfo(info: Partial) {
+ return {
+ chartType: ChartType.LINE,
+ series: [],
+ timeseriesResult: {} as any,
+ yAxis: '',
+ ...info,
+ };
+ }
+
+ it('loading', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('loading-placeholder')).toBeInTheDocument();
+ });
+
+ describe('with raw counts', () => {
+ describe('unsampled', () => {
+ describe('without user query', () => {
+ it('loaded without top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent('100 logs');
+ });
+
+ it('loaded with top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 logs for top 5 groups'
+ );
+ });
+ });
+
+ describe('with user query', () => {
+ it('loaded without top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 matches of 1k logs'
+ );
+ });
+
+ it('loaded with top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 matches of 1k logs for top 5 groups'
+ );
+ });
+ });
+ });
+
+ describe('sampled', () => {
+ describe('without user query', () => {
+ describe('partial scan', () => {
+ it('loaded 1', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 sample of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 sample')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 sample of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 sample')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 samples of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 samples')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 samples of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 samples')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('full scan', () => {
+ it('loaded 1', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 log'
+ );
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 log'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 logs'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 logs'
+ );
+ });
+ });
+ });
+
+ describe('with user query', () => {
+ describe('partial scan', () => {
+ it('loaded 1', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 match after scanning 100 samples of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 match')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 match after scanning 100 samples of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 match')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 matches after scanning 100 samples of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 matches')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 matches after scanning 100 samples of 1k logs'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 matches')
+ );
+ expect(
+ await screen.findByText(
+ /The volume of logs in this time range is too large for us to do a full scan./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /Try reducing the date range or number of projects to attempt scanning all logs./
+ )
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('full scan', () => {
+ it('loaded 1', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 log'
+ );
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 log'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 logs'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 logs'
+ );
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/static/app/views/explore/logs/confidenceFooter.tsx b/static/app/views/explore/logs/confidenceFooter.tsx
index 9b2861ee1e8660..e02da34d464467 100644
--- a/static/app/views/explore/logs/confidenceFooter.tsx
+++ b/static/app/views/explore/logs/confidenceFooter.tsx
@@ -3,10 +3,7 @@ import Count from 'sentry/components/count';
import {t, tct} from 'sentry/locale';
import type {Confidence} from 'sentry/types/organization';
import {defined} from 'sentry/utils';
-import {
- Container,
- usePreviouslyLoaded,
-} from 'sentry/views/explore/components/chart/chartFooter';
+import {Container} from 'sentry/views/explore/components/chart/chartFooter';
import {
Placeholder,
WarningIcon,
@@ -22,12 +19,11 @@ interface ConfidenceFooterProps {
}
export function ConfidenceFooter({
- chartInfo: currentChartInfo,
+ chartInfo,
hasUserQuery,
isLoading,
rawLogCounts,
}: ConfidenceFooterProps) {
- const chartInfo = usePreviouslyLoaded(currentChartInfo, isLoading);
return (
1;
-
- if (!defined(sampleCount) || isLoading) {
+ if (isLoading || !defined(sampleCount)) {
return ;
}
+ const isTopN = defined(topEvents) && topEvents > 1;
const noSampling = defined(isSampled) && !isSampled;
- const matchingLogsCount =
- sampleCount > 1
- ? t('%s matches', )
- : t('%s match', );
- const downsampledLogsCount = rawLogCounts.normal.count ? (
- rawLogCounts.normal.count > 1 ? (
- t('%s samples', )
- ) : (
- t('%s sample', )
- )
- ) : (
-
- );
- const allLogsCount = rawLogCounts.highAccuracy.count ? (
- rawLogCounts.highAccuracy.count > 1 ? (
- t('%s logs', )
- ) : (
- t('%s log', )
- )
- ) : (
-
- );
- if (dataScanned === 'full') {
+ // No sampling happened, so don't mention estimations.
+ if (noSampling) {
if (!hasUserQuery) {
+ const matchingLogsCount =
+ sampleCount > 1
+ ? t('%s logs', )
+ : t('%s log', );
+
if (isTopN) {
- return tct('Log count for top [topEvents] groups: [matchingLogsCount]', {
+ return tct('[matchingLogsCount] for top [topEvents] groups', {
+ matchingLogsCount,
topEvents,
- matchingLogsCount: ,
});
}
- return tct('Log count: [matchingLogsCount]', {
- matchingLogsCount: ,
- });
+ return matchingLogsCount;
}
- // For logs, if the full data was scanned, we can assume that no
- // extrapolation happened and we should remove mentions of extrapolation.
+ const matchingLogsCount =
+ sampleCount > 1
+ ? t('%s matches', )
+ : t('%s match', );
+
+ const totalLogsCount = rawLogCounts.highAccuracy.count ? (
+ rawLogCounts.highAccuracy.count > 1 ? (
+ t('%s logs', )
+ ) : (
+ t('%s log', )
+ )
+ ) : (
+
+ );
+
if (isTopN) {
- return tct('[matchingLogsCount] for top [topEvents] groups in [allLogsCount]', {
- topEvents,
+ return tct('[matchingLogsCount] of [totalLogsCount] for top [topEvents] groups', {
matchingLogsCount,
- allLogsCount,
+ totalLogsCount,
+ topEvents,
});
}
- return tct('[matchingLogsCount] in [allLogsCount]', {
+ return tct('[matchingLogsCount] of [totalLogsCount]', {
matchingLogsCount,
- allLogsCount,
+ totalLogsCount,
});
}
- const downsampledTooltip = ;
+ const maybeWarning =
+ dataScanned === 'partial' ? tct('[warning] ', {warning: }) : null;
+ const maybeTooltip =
+ dataScanned === 'partial' ? : null;
+
+ // no user query means it's showing the total number of logs scanned
+ // so no need to mention how many matched
+ if (!hasUserQuery) {
+ // partial scans means that we didnt scan all the data so it's useful
+ // to mention the total number of logs available
+ if (dataScanned === 'partial') {
+ const matchingLogsCount =
+ sampleCount > 1
+ ? t('%s samples', )
+ : t('%s sample', );
+
+ const totalLogsCount = rawLogCounts.highAccuracy.count ? (
+ rawLogCounts.highAccuracy.count > 1 ? (
+ t('%s logs', )
+ ) : (
+ t('%s log', )
+ )
+ ) : (
+
+ );
+
+ if (isTopN) {
+ return tct(
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingLogsCount]] of [totalLogsCount]',
+ {
+ maybeWarning,
+ topEvents,
+ maybeTooltip,
+ matchingLogsCount,
+ totalLogsCount,
+ }
+ );
+ }
+
+ return tct(
+ '[maybeWarning]Estimated from [maybeTooltip:[matchingLogsCount]] of [totalLogsCount]',
+ {
+ maybeWarning,
+ maybeTooltip,
+ matchingLogsCount,
+ totalLogsCount,
+ }
+ );
+ }
+
+ // otherwise, a full scan was done
+ // full scan means we scanned all the data available so no need to repeat that information twice
+
+ const matchingLogsCount =
+ sampleCount > 1
+ ? t('%s logs', )
+ : t('%s log', );
+
+ if (isTopN) {
+ return tct(
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingLogsCount]]',
+ {
+ maybeWarning,
+ topEvents,
+ maybeTooltip,
+ matchingLogsCount,
+ }
+ );
+ }
+
+ return tct('[maybeWarning]Estimated from [maybeTooltip:[matchingLogsCount]]', {
+ maybeWarning,
+ maybeTooltip,
+ matchingLogsCount,
+ });
+ }
+
+ // otherwise, a user query was specified
+ // with a user query, it means we should tell the user how many of the scanned logs
+ // matched the user query
+
+ // partial scans means that we didnt scan all the data so it's useful
+ // to mention the total number of logs available
+ if (dataScanned === 'partial') {
+ const matchingLogsCount =
+ sampleCount > 1
+ ? t('%s matches', )
+ : t('%s match', );
+
+ const scannedLogsCount = rawLogCounts.normal.count ? (
+ rawLogCounts.normal.count > 1 ? (
+ t('%s samples', )
+ ) : (
+ t('%s sample', )
+ )
+ ) : (
+
+ );
+
+ const totalLogsCount = rawLogCounts.highAccuracy.count ? (
+ rawLogCounts.highAccuracy.count > 1 ? (
+ t('%s logs', )
+ ) : (
+ t('%s log', )
+ )
+ ) : (
+
+ );
+
+ if (isTopN) {
+ return tct(
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingLogsCount]] after scanning [scannedLogsCount] of [totalLogsCount]',
+ {
+ maybeWarning,
+ topEvents,
+ maybeTooltip,
+ matchingLogsCount,
+ scannedLogsCount,
+ totalLogsCount,
+ }
+ );
+ }
+
+ return tct(
+ '[maybeWarning]Estimated from [maybeTooltip:[matchingLogsCount]] after scanning [scannedLogsCount] of [totalLogsCount]',
+ {
+ maybeWarning,
+ maybeTooltip,
+ matchingLogsCount,
+ scannedLogsCount,
+ totalLogsCount,
+ }
+ );
+ }
+
+ // otherwise, a full scan was done
+ // full scan means we scanned all the data available so no need to repeat that information twice
+
+ const matchingLogsCount =
+ sampleCount > 1
+ ? t('%s matches', )
+ : t('%s match', );
+
+ const totalLogsCount = rawLogCounts.highAccuracy.count ? (
+ rawLogCounts.highAccuracy.count > 1 ? (
+ t('%s logs', )
+ ) : (
+ t('%s log', )
+ )
+ ) : (
+
+ );
if (isTopN) {
return tct(
- '[warning] Extrapolated from [matchingLogsCount] for top [topEvents] groups after scanning [tooltip:[downsampledLogsCount] of [allLogsCount]]',
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingLogsCount]] of [totalLogsCount]',
{
- warning: ,
+ maybeWarning,
topEvents,
+ maybeTooltip,
matchingLogsCount,
- downsampledLogsCount,
- allLogsCount,
- tooltip: downsampledTooltip,
+ totalLogsCount,
}
);
}
return tct(
- '[warning] Extrapolated from [matchingLogsCount] after scanning [tooltip:[downsampledLogsCount] of [allLogsCount]]',
+ '[maybeWarning]Estimated from [maybeTooltip:[matchingLogsCount]] of [totalLogsCount]',
{
- warning: ,
+ maybeWarning,
+ maybeTooltip,
matchingLogsCount,
- downsampledLogsCount,
- allLogsCount,
- tooltip: downsampledTooltip,
+ totalLogsCount,
}
);
}
@@ -168,6 +307,7 @@ function DownsampledTooltip({
'The volume of logs in this time range is too large for us to do a full scan.'
)}
+
{t(
'Try reducing the date range or number of projects to attempt scanning all logs.'
)}
diff --git a/static/app/views/explore/spans/charts/confidenceFooter.spec.tsx b/static/app/views/explore/spans/charts/confidenceFooter.spec.tsx
index bedb365866c2e1..94e620333ec9d5 100644
--- a/static/app/views/explore/spans/charts/confidenceFooter.spec.tsx
+++ b/static/app/views/explore/spans/charts/confidenceFooter.spec.tsx
@@ -7,105 +7,1039 @@ function Wrapper({children}: {children: React.ReactNode}) {
}
describe('ConfidenceFooter', () => {
- describe('low confidence', () => {
- it('renders for full scan without grouping', async () => {
- render(
- ,
- {wrapper: Wrapper}
- );
-
- expect(screen.getByTestId('wrapper')).toHaveTextContent(
- 'Extrapolated from 100 spans'
- );
- await userEvent.hover(screen.getByText('100'));
- expect(
- await screen.findByText(
- /You may not have enough span samples for a high accuracy extrapolation of your query./
- )
- ).toBeInTheDocument();
- });
- it('renders for full scan with grouping', async () => {
- render(
- ,
- {wrapper: Wrapper}
- );
-
- expect(screen.getByTestId('wrapper')).toHaveTextContent(
- 'Extrapolated from 100 spans for top 5 groups'
- );
- await userEvent.hover(screen.getByText('100'));
- expect(
- await screen.findByText(
- /You may not have enough span samples for a high accuracy extrapolation of your query./
- )
- ).toBeInTheDocument();
- });
+ it('loading', () => {
+ render(, {wrapper: Wrapper});
+ expect(screen.getByTestId('loading-placeholder')).toBeInTheDocument();
});
- describe('high confidence', () => {
- it('renders for full scan without grouping', () => {
- render(
- ,
- {wrapper: Wrapper}
- );
-
- expect(screen.getByTestId('wrapper')).toHaveTextContent('Span count: 100');
+ describe('without raw counts', () => {
+ describe('low confidence', () => {
+ it('loaded 1', async () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent('Estimated from 1 span');
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 span')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 span'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 span')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 spans')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 spans')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
});
- it('renders for full scan with grouping', () => {
- render(
- ,
- {wrapper: Wrapper}
- );
-
- expect(screen.getByTestId('wrapper')).toHaveTextContent(
- 'Span count for top 5 groups: 100'
- );
+
+ describe('high confidence', () => {
+ it('loaded 1', () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent('Estimated from 1 span');
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 span'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 spans'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(, {
+ wrapper: Wrapper,
+ });
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 spans'
+ );
+ });
});
});
- describe('unextrapolated', () => {
- it('unextrapolated loading', () => {
- render(, {wrapper: Wrapper});
+ describe('with raw counts', () => {
+ const rawSpanCounts = {
+ normal: {
+ count: 100,
+ isLoading: false,
+ },
+ highAccuracy: {
+ count: 1000,
+ isLoading: false,
+ },
+ };
+
+ describe('unextrapolated', () => {
+ describe('without user query', () => {
+ it('loaded without top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent('100 spans');
+ });
+
+ it('loaded with top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 spans for top 5 groups'
+ );
+ });
+ });
+
+ describe('with user query', () => {
+ it('loaded without top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 matches of 1k spans'
+ );
+ });
- expect(screen.getByTestId('loading-placeholder')).toBeInTheDocument();
+ it('loaded with top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 matches of 1k spans for top 5 groups'
+ );
+ });
+ });
});
- it('unextrapolated loaded', () => {
- render(, {
- wrapper: Wrapper,
+ describe('unsampled', () => {
+ describe('without user query', () => {
+ it('loaded without top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent('100 spans');
+ });
+
+ it('loaded with top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 spans for top 5 groups'
+ );
+ });
});
- expect(screen.getByTestId('wrapper')).toHaveTextContent('Span count: 100');
+ describe('with user query', () => {
+ it('loaded without top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 matches of 1k spans'
+ );
+ });
+
+ it('loaded with top events', () => {
+ render(
+ ,
+ {
+ wrapper: Wrapper,
+ }
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ '100 matches of 1k spans for top 5 groups'
+ );
+ });
+ });
});
- it('unextrapolated loaded with grouping', () => {
- render(, {
- wrapper: Wrapper,
+ describe('without user query', () => {
+ describe('partial scan', () => {
+ describe('low confidence', () => {
+ it('loaded 1', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 sample of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 sample')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 sample of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 sample')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 samples of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 samples')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 samples of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 samples')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('high confidence', () => {
+ it('loaded 1', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 sample of 1k spans'
+ );
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 sample of 1k spans'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 samples of 1k spans'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 samples of 1k spans'
+ );
+ });
+ });
});
- expect(screen.getByTestId('wrapper')).toHaveTextContent(
- 'Span count for top 5 groups: 100'
- );
+ describe('full scan', () => {
+ describe('low confidence', () => {
+ it('loaded 1', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 span'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 span')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 span'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 span')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 spans')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 spans')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('high confidence', () => {
+ it('loaded 1', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 span'
+ );
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 span'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 spans'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 spans'
+ );
+ });
+ });
+ });
+ });
+
+ describe('with user query', () => {
+ describe('partial scan', () => {
+ describe('low confidence', () => {
+ it('loaded 1', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 match after scanning 100 samples of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 match')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range, removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 match after scanning 100 samples of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 match')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range, removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 matches after scanning 100 samples of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 matches')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range, removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 matches after scanning 100 samples of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 matches')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by narrowing the date range, removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('high confidence', () => {
+ it('loaded 1', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 match after scanning 100 samples of 1k spans'
+ );
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 match after scanning 100 samples of 1k spans'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 matches after scanning 100 samples of 1k spans'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 matches after scanning 100 samples of 1k spans'
+ );
+ });
+ });
+ });
+
+ describe('full scan', () => {
+ describe('low confidence', () => {
+ it('loaded 1', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 match of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 match')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 1 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 match of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '1 match')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 matches of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 matches')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+
+ it('loaded 10 with grouping', async () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 matches of 1k spans'
+ );
+ await userEvent.hover(
+ screen.getByText((_, element) => element?.textContent === '10 matches')
+ );
+ expect(
+ await screen.findByText(
+ /You may not have enough span samples for a high accuracy estimation of your query./
+ )
+ ).toBeInTheDocument();
+ expect(
+ await screen.findByText(
+ /You can try adjusting your query by removing filters or increasing the chart's time interval./
+ )
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('high confidence', () => {
+ it('loaded 1', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 1 match of 1k spans'
+ );
+ });
+
+ it('loaded 1 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 1 match of 1k spans'
+ );
+ });
+
+ it('loaded 10', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated from 10 matches of 1k spans'
+ );
+ });
+
+ it('loaded 10 with grouping', () => {
+ render(
+ ,
+ {wrapper: Wrapper}
+ );
+ expect(screen.getByTestId('wrapper')).toHaveTextContent(
+ 'Estimated for top 5 groups from 10 matches of 1k spans'
+ );
+ });
+ });
+ });
});
});
});
diff --git a/static/app/views/explore/spans/charts/confidenceFooter.tsx b/static/app/views/explore/spans/charts/confidenceFooter.tsx
index 96db5f43123660..e32d1531dee45c 100644
--- a/static/app/views/explore/spans/charts/confidenceFooter.tsx
+++ b/static/app/views/explore/spans/charts/confidenceFooter.tsx
@@ -6,7 +6,6 @@ import Count from 'sentry/components/count';
import {t, tct} from 'sentry/locale';
import type {Confidence} from 'sentry/types/organization';
import {defined} from 'sentry/utils';
-import usePrevious from 'sentry/utils/usePrevious';
import {
Placeholder,
WarningIcon,
@@ -26,13 +25,11 @@ type Props = {
};
export function ConfidenceFooter(props: Props) {
- const previousProps = usePrevious(props, props.isLoading);
- return (
- {confidenceMessage(props.isLoading ? previousProps : props)}
- );
+ return {confidenceMessage(props)};
}
function confidenceMessage({
+ dataScanned,
extrapolate,
rawSpanCounts,
sampleCount,
@@ -42,135 +39,270 @@ function confidenceMessage({
isLoading,
userQuery,
}: Props) {
- const isTopN = defined(topEvents) && topEvents > 1;
-
- if (!defined(sampleCount) || isLoading) {
+ if (isLoading || !defined(sampleCount)) {
return ;
}
- if (
- // Extrapolation disabled, so don't mention extrapolation.
- (defined(extrapolate) && !extrapolate) ||
- // High confidence without user query means we're in a default query.
- // We check for high confidence here because we still want to show the
- // tooltip here if it's low confidence
- (confidence === 'high' && !userQuery)
- ) {
- return isTopN
- ? tct('Span count for top [topEvents] groups: [matchingSpansCount]', {
- topEvents,
- matchingSpansCount: ,
- })
- : tct('Span count: [matchingSpansCount]', {
- matchingSpansCount: ,
- });
- }
-
+ const isTopN = defined(topEvents) && topEvents > 1;
const noSampling = defined(isSampled) && !isSampled;
- const lowAccuracyFullSampleCount = <_LowAccuracyFullTooltip noSampling={noSampling} />;
+
+ const maybeWarning =
+ confidence === 'low' ? tct('[warning] ', {warning: }) : null;
+ const maybeTooltip =
+ confidence === 'low' ? (
+ <_LowAccuracyFullTooltip
+ noSampling={noSampling}
+ dataScanned={dataScanned}
+ userQuery={userQuery}
+ />
+ ) : null;
// The multi query mode does not fetch the raw span counts
// so make sure to have a backup when this happens.
if (!defined(rawSpanCounts)) {
const matchingSpansCount =
- sampleCount > 1
- ? t('%s spans', )
- : t('%s span', );
+ sampleCount === 1
+ ? t('%s span', )
+ : t('%s spans', );
+
+ if (isTopN) {
+ return tct(
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingSpansCount]]',
+ {
+ maybeWarning,
+ topEvents,
+ maybeTooltip,
+ matchingSpansCount,
+ }
+ );
+ }
+
+ return tct('[maybeWarning]Estimated from [maybeTooltip:[matchingSpansCount]]', {
+ maybeWarning,
+ maybeTooltip,
+ matchingSpansCount,
+ });
+ }
+
+ if (
+ // Extrapolation disabled, so don't mention estimations.
+ (defined(extrapolate) && !extrapolate) ||
+ // No sampling happened, so don't mention estimations.
+ noSampling
+ ) {
+ if (!userQuery) {
+ const matchingSpansCount =
+ sampleCount > 1
+ ? t('%s spans', )
+ : t('%s span', );
- if (confidence === 'high') {
if (isTopN) {
- return tct('Extrapolated from [matchingSpansCount] for top [topEvents] groups', {
+ return tct('[matchingSpansCount] for top [topEvents] groups', {
topEvents,
matchingSpansCount,
});
}
- return tct('Extrapolated from [matchingSpansCount]', {
+ return matchingSpansCount;
+ }
+
+ const matchingSpansCount =
+ sampleCount > 1
+ ? t('%s matches', )
+ : t('%s match', );
+
+ const totalSpansCount = rawSpanCounts.highAccuracy.count ? (
+ rawSpanCounts.highAccuracy.count > 1 ? (
+ t('%s spans', )
+ ) : (
+ t('%s span', )
+ )
+ ) : (
+
+ );
+
+ if (isTopN) {
+ return tct('[matchingSpansCount] of [totalSpansCount] for top [topEvents] groups', {
matchingSpansCount,
+ totalSpansCount,
+ topEvents,
});
}
+ return tct('[matchingSpansCount] of [totalSpansCount]', {
+ matchingSpansCount,
+ totalSpansCount,
+ });
+ }
+
+ // no user query means it's showing the total number of spans scanned
+ // so no need to mention how many matched
+ if (!userQuery) {
+ // partial scans means that we didnt scan all the data so it's useful
+ // to mention the total number of spans available
+ if (dataScanned === 'partial') {
+ const matchingSpansCount =
+ sampleCount > 1
+ ? t('%s samples', )
+ : t('%s sample', );
+
+ const totalSpansCount = rawSpanCounts.highAccuracy.count ? (
+ rawSpanCounts.highAccuracy.count > 1 ? (
+ t('%s spans', )
+ ) : (
+ t('%s span', )
+ )
+ ) : (
+
+ );
+
+ if (isTopN) {
+ return tct(
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingSpansCount]] of [totalSpansCount]',
+ {
+ maybeWarning,
+ topEvents,
+ maybeTooltip,
+ matchingSpansCount,
+ totalSpansCount,
+ }
+ );
+ }
+
+ return tct(
+ '[maybeWarning]Estimated from [maybeTooltip:[matchingSpansCount]] of [totalSpansCount]',
+ {
+ maybeWarning,
+ maybeTooltip,
+ matchingSpansCount,
+ totalSpansCount,
+ }
+ );
+ }
+
+ // otherwise, a full scan was done
+ // full scan means we scanned all the data available so no need to repeat that information twice
+
+ const matchingSpansCount =
+ sampleCount > 1
+ ? t('%s spans', )
+ : t('%s span', );
+
if (isTopN) {
return tct(
- 'Extrapolated from [tooltip:[matchingSpansCount]] for top [topEvents] groups',
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingSpansCount]]',
{
+ maybeWarning,
+ maybeTooltip,
topEvents,
matchingSpansCount,
- tooltip: lowAccuracyFullSampleCount,
}
);
}
- return tct('Extrapolated from [tooltip:[matchingSpansCount]]', {
+ return tct('[maybeWarning]Estimated from [maybeTooltip:[matchingSpansCount]]', {
+ maybeWarning,
+ maybeTooltip,
matchingSpansCount,
- tooltip: lowAccuracyFullSampleCount,
});
}
- const matchingSpansCount =
- sampleCount > 1
- ? t('%s matches', )
- : t('%s match', );
+ // otherwise, a user query was specified
+ // with a user query, it means we should tell the user how many of the scanned spans
+ // matched the user query
- const downSampledSpansCount = rawSpanCounts.normal.count ? (
- rawSpanCounts.normal.count > 1 ? (
- t('%s samples', )
+ // partial scans means that we didnt scan all the data so it's useful
+ // to mention the total number of spans available
+ if (dataScanned === 'partial') {
+ const matchingSpansCount =
+ sampleCount > 1
+ ? t('%s matches', )
+ : t('%s match', );
+
+ const scannedSpansCount = rawSpanCounts.normal.count ? (
+ rawSpanCounts.normal.count > 1 ? (
+ t('%s samples', )
+ ) : (
+ t('%s sample', )
+ )
) : (
- t('%s sample', )
- )
- ) : (
-
- );
- const allSpansCount = rawSpanCounts.highAccuracy.count ? (
- rawSpanCounts.highAccuracy.count > 1 ? (
- t('%s spans', )
+
+ );
+
+ const totalSpansCount = rawSpanCounts.highAccuracy.count ? (
+ rawSpanCounts.highAccuracy.count > 1 ? (
+ t('%s spans', )
+ ) : (
+ t('%s span', )
+ )
) : (
- t('%s span', )
- )
- ) : (
-
- );
+
+ );
- if (confidence === 'high') {
if (isTopN) {
return tct(
- 'Extrapolated from [matchingSpansCount] for top [topEvents] groups in [allSpansCount]',
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingSpansCount]] after scanning [scannedSpansCount] of [totalSpansCount]',
{
- topEvents,
+ maybeWarning,
+ maybeTooltip,
matchingSpansCount,
- allSpansCount,
+ scannedSpansCount,
+ totalSpansCount,
+ topEvents,
}
);
}
- return tct('Extrapolated from [matchingSpansCount] in [allSpansCount]', {
- matchingSpansCount,
- allSpansCount,
- });
+ return tct(
+ '[maybeWarning]Estimated from [maybeTooltip:[matchingSpansCount]] after scanning [scannedSpansCount] of [totalSpansCount]',
+ {
+ maybeWarning,
+ maybeTooltip,
+ matchingSpansCount,
+ scannedSpansCount,
+ totalSpansCount,
+ }
+ );
}
+ // otherwise, a full scan was done
+ // full scan means we scanned all the data available so no need to repeat that information twice
+
+ const matchingSpansCount =
+ sampleCount > 1
+ ? t('%s matches', )
+ : t('%s match', );
+
+ const totalSpansCount = rawSpanCounts.highAccuracy.count ? (
+ rawSpanCounts.highAccuracy.count > 1 ? (
+ t('%s spans', )
+ ) : (
+ t('%s span', )
+ )
+ ) : (
+
+ );
+
if (isTopN) {
return tct(
- '[warning] Extrapolated from [matchingSpansCount] for top [topEvents] groups after scanning [tooltip:[downSampledSpansCount] of [allSpansCount]]',
+ '[maybeWarning]Estimated for top [topEvents] groups from [maybeTooltip:[matchingSpansCount]] of [totalSpansCount]',
{
- warning: ,
+ maybeWarning,
topEvents,
+ maybeTooltip,
matchingSpansCount,
- downSampledSpansCount,
- allSpansCount,
- tooltip: lowAccuracyFullSampleCount,
+ totalSpansCount,
}
);
}
return tct(
- '[warning] Extrapolated from [matchingSpansCount] after scanning [tooltip:[downSampledSpansCount] of [allSpansCount]]',
+ '[maybeWarning]Estimated from [maybeTooltip:[matchingSpansCount]] of [totalSpansCount]',
{
- warning: ,
+ maybeWarning,
+ maybeTooltip,
matchingSpansCount,
- downSampledSpansCount,
- allSpansCount,
- tooltip: lowAccuracyFullSampleCount,
+ totalSpansCount,
}
);
}
@@ -178,24 +310,42 @@ function confidenceMessage({
function _LowAccuracyFullTooltip({
noSampling,
children,
+ dataScanned,
+ userQuery,
}: {
noSampling: boolean;
children?: React.ReactNode;
+ dataScanned?: 'full' | 'partial';
+ userQuery?: string;
}) {
return (
{t(
- 'You may not have enough span samples for a high accuracy extrapolation of your query.'
+ 'You may not have enough span samples for a high accuracy estimation of your query.'
)}
- {t(
- "You can try adjusting your query by narrowing the date range, removing filters or increasing the chart's time interval."
- )}
+
+ {dataScanned === 'partial' && userQuery
+ ? t(
+ "You can try adjusting your query by narrowing the date range, removing filters or increasing the chart's time interval."
+ )
+ : dataScanned === 'partial'
+ ? t(
+ "You can try adjusting your query by narrowing the date range or increasing the chart's time interval."
+ )
+ : userQuery
+ ? t(
+ "You can try adjusting your query by removing filters or increasing the chart's time interval."
+ )
+ : t(
+ "You can try adjusting your query by increasing the chart's time interval."
+ )}
{/* Do not show if no sampling happened to the data points in the series as they are already at 100% sampling */}
{!noSampling && (
+
{t(
'You can also increase your sampling rates to get more samples and accurate trends.'
@@ -204,7 +354,7 @@ function _LowAccuracyFullTooltip({
)}
}
- maxWidth={270}
+ maxWidth={300}
showUnderline
>
{children}