diff --git a/static/app/utils/analytics/tracingEventMap.tsx b/static/app/utils/analytics/tracingEventMap.tsx index 0d8268b601334f..3166487618c16a 100644 --- a/static/app/utils/analytics/tracingEventMap.tsx +++ b/static/app/utils/analytics/tracingEventMap.tsx @@ -1,3 +1,4 @@ +import type {Confidence} from 'sentry/types/organization'; import type {Visualize} from 'sentry/views/explore/contexts/pageParamsContext/visualizes'; export type TracingEventParameters = { @@ -7,11 +8,14 @@ export type TracingEventParameters = { 'trace.explorer.metadata': { columns: string[]; columns_count: number; + confidences: Confidence[]; + dataset: string; has_exceeded_performance_usage_limit: boolean | null; - query_status: 'success' | 'error'; + query_status: 'success' | 'error' | 'pending'; result_length: number; result_missing_root: number; result_mode: 'trace samples' | 'span samples' | 'aggregates'; + title: string; user_queries: string; user_queries_count: number; visualizes: Visualize[]; diff --git a/static/app/views/explore/charts/index.tsx b/static/app/views/explore/charts/index.tsx index f940a3fbff0b07..55a9647809c441 100644 --- a/static/app/views/explore/charts/index.tsx +++ b/static/app/views/explore/charts/index.tsx @@ -300,6 +300,7 @@ export function useExtrapolationMeta({ initialData: [], referrer: 'api.explore.spans-extrapolation-meta', enabled: dataset === DiscoverDatasets.SPANS_EAP_RPC, + trackResponseAnalytics: false, }); } diff --git a/static/app/views/explore/hooks/useAnalytics.tsx b/static/app/views/explore/hooks/useAnalytics.tsx index fd2a26586035bc..f3b9fbf75bd6ab 100644 --- a/static/app/views/explore/hooks/useAnalytics.tsx +++ b/static/app/views/explore/hooks/useAnalytics.tsx @@ -1,82 +1,207 @@ import {useEffect} from 'react'; -import type {Confidence, Organization} from 'sentry/types/organization'; +import {defined} from 'sentry/utils'; import {trackAnalytics} from 'sentry/utils/analytics'; -import type {DiscoverDatasets} from 'sentry/utils/discover/types'; -import type {UseApiQueryResult} from 'sentry/utils/queryClient'; -import type RequestError from 'sentry/utils/requestError/requestError'; +import {dedupeArray} from 'sentry/utils/dedupeArray'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import useOrganization from 'sentry/utils/useOrganization'; +import { + useExploreDataset, + useExploreFields, + useExploreQuery, + useExploreTitle, + useExploreVisualizes, +} from 'sentry/views/explore/contexts/pageParamsContext'; import type {Visualize} from 'sentry/views/explore/contexts/pageParamsContext/visualizes'; +import type {AggregatesTableResult} from 'sentry/views/explore/hooks/useExploreAggregatesTable'; +import type {SpansTableResult} from 'sentry/views/explore/hooks/useExploreSpansTable'; +import type {TracesTableResult} from 'sentry/views/explore/hooks/useExploreTracesTable'; +import {combineConfidenceForSeries} from 'sentry/views/explore/utils'; +import type {useSortedTimeSeries} from 'sentry/views/insights/common/queries/useSortedTimeSeries'; import {usePerformanceSubscriptionDetails} from 'sentry/views/performance/newTraceDetails/traceTypeWarnings/usePerformanceSubscriptionDetails'; export function useAnalytics({ - dataset, - resultLength, - resultMissingRoot, - resultMode, - resultStatus, - visualizes, - organization, - columns, - userQuery, - confidences, - title, + queryType, + aggregatesTableResult, + spansTableResult, + tracesTableResult, + timeseriesResult, }: { - columns: string[]; - confidences: Confidence[]; - dataset: DiscoverDatasets; - organization: Organization; - resultLength: number | undefined; - resultMode: 'span samples' | 'trace samples' | 'aggregates'; - resultStatus: UseApiQueryResult['status']; - userQuery: string; - visualizes: Visualize[]; - resultMissingRoot?: number; - title?: string; + aggregatesTableResult: AggregatesTableResult; + queryType: 'aggregate' | 'samples' | 'traces'; + spansTableResult: SpansTableResult; + timeseriesResult: ReturnType; + tracesTableResult: TracesTableResult; }) { + const organization = useOrganization(); + const dataset = useExploreDataset(); + const title = useExploreTitle(); + const query = useExploreQuery(); + const fields = useExploreFields(); + const visualizes = useExploreVisualizes(); + const { data: {hasExceededPerformanceUsageLimit}, isLoading: isLoadingSubscriptionDetails, } = usePerformanceSubscriptionDetails(); useEffect(() => { - if (resultStatus === 'pending' || isLoadingSubscriptionDetails) { + if ( + queryType !== 'aggregate' || + aggregatesTableResult.result.isPending || + timeseriesResult.isPending || + isLoadingSubscriptionDetails + ) { return; } - const search = new MutableSearch(userQuery); - const params = { + const search = new MutableSearch(query); + const columns = aggregatesTableResult.eventView.getColumns() as unknown as string[]; + trackAnalytics('trace.explorer.metadata', { organization, - columns, - columns_count: columns.filter(Boolean).length, - confidences, dataset, - query_status: resultStatus, - result_length: resultLength || 0, - result_missing_root: resultMissingRoot || 0, - result_mode: resultMode, + result_mode: 'aggregates', + columns, + columns_count: columns.length, + query_status: aggregatesTableResult.result.status, + result_length: aggregatesTableResult.result.data?.length || 0, + result_missing_root: 0, user_queries: search.formatString(), user_queries_count: search.tokens.length, visualizes, visualizes_count: visualizes.length, - title, + title: title || '', + confidences: computeConfidence(visualizes, timeseriesResult.data), has_exceeded_performance_usage_limit: hasExceededPerformanceUsageLimit, - }; - - trackAnalytics('trace.explorer.metadata', params); + }); }, [ organization, - resultLength, - resultMissingRoot, - resultMode, - isLoadingSubscriptionDetails, + dataset, + fields, + query, + visualizes, + title, + queryType, + aggregatesTableResult.result.isPending, + aggregatesTableResult.result.status, + aggregatesTableResult.result.data?.length, + aggregatesTableResult.eventView, + timeseriesResult.isPending, + timeseriesResult.data, hasExceededPerformanceUsageLimit, - resultStatus, + isLoadingSubscriptionDetails, + ]); + + useEffect(() => { + if ( + queryType !== 'samples' || + spansTableResult.result.isPending || + timeseriesResult.isPending || + isLoadingSubscriptionDetails + ) { + return; + } + + const search = new MutableSearch(query); + trackAnalytics('trace.explorer.metadata', { + organization, + dataset, + result_mode: 'span samples', + columns: fields, + columns_count: fields.length, + query_status: spansTableResult.result.status, + result_length: spansTableResult.result.data?.length || 0, + result_missing_root: 0, + user_queries: search.formatString(), + user_queries_count: search.tokens.length, + visualizes, + visualizes_count: visualizes.length, + title: title || '', + confidences: computeConfidence(visualizes, timeseriesResult.data), + has_exceeded_performance_usage_limit: hasExceededPerformanceUsageLimit, + }); + }, [ + organization, + dataset, + fields, + query, visualizes, - columns, - userQuery, - confidences, + title, + queryType, + spansTableResult.result.isPending, + spansTableResult.result.status, + spansTableResult.result.data?.length, + timeseriesResult.isPending, + timeseriesResult.data, + hasExceededPerformanceUsageLimit, + isLoadingSubscriptionDetails, + ]); + + const resultMissingRoot = + tracesTableResult.result?.data?.data?.filter(trace => defined(trace.name)).length ?? + 0; + useEffect(() => { + if ( + queryType !== 'traces' || + tracesTableResult.result.isPending || + timeseriesResult.isPending || + isLoadingSubscriptionDetails + ) { + return; + } + + const search = new MutableSearch(query); + const columns = [ + 'trace id', + 'trace root', + 'total spans', + 'timeline', + 'root duration', + 'timestamp', + ]; + trackAnalytics('trace.explorer.metadata', { + organization, + dataset, + result_mode: 'trace samples', + columns, + columns_count: columns.length, + query_status: tracesTableResult.result.status, + result_length: tracesTableResult.result.data?.data?.length || 0, + result_missing_root: resultMissingRoot, + user_queries: search.formatString(), + user_queries_count: search.tokens.length, + visualizes, + visualizes_count: visualizes.length, + title: title || '', + confidences: computeConfidence(visualizes, timeseriesResult.data), + has_exceeded_performance_usage_limit: hasExceededPerformanceUsageLimit, + }); + }, [ + organization, dataset, + fields, + query, + visualizes, title, + queryType, + tracesTableResult.result.isPending, + tracesTableResult.result.status, + tracesTableResult.result.data?.data?.length, + resultMissingRoot, + timeseriesResult.isPending, + timeseriesResult.data, + hasExceededPerformanceUsageLimit, + isLoadingSubscriptionDetails, ]); } + +function computeConfidence( + visualizes: Visualize[], + data: ReturnType['data'] +) { + return visualizes.map(visualize => { + const dedupedYAxes = dedupeArray(visualize.yAxes); + const series = dedupedYAxes.flatMap(yAxis => data[yAxis]).filter(defined); + return combineConfidenceForSeries(series); + }); +} diff --git a/static/app/views/explore/hooks/useExploreAggregatesTable.tsx b/static/app/views/explore/hooks/useExploreAggregatesTable.tsx index 1f83525c09d67a..01c2ccd21ca0fb 100644 --- a/static/app/views/explore/hooks/useExploreAggregatesTable.tsx +++ b/static/app/views/explore/hooks/useExploreAggregatesTable.tsx @@ -88,6 +88,7 @@ export function useExploreAggregatesTable({ initialData: [], limit, referrer: 'api.explore.spans-aggregates-table', + trackResponseAnalytics: false, }); return useMemo(() => { diff --git a/static/app/views/explore/hooks/useExploreSpansTable.tsx b/static/app/views/explore/hooks/useExploreSpansTable.tsx index 5090bab6289eb1..2633bdbb0cbc1e 100644 --- a/static/app/views/explore/hooks/useExploreSpansTable.tsx +++ b/static/app/views/explore/hooks/useExploreSpansTable.tsx @@ -75,6 +75,7 @@ export function useExploreSpansTable({ limit, referrer: 'api.explore.spans-samples-table', allowAggregateConditions: false, + trackResponseAnalytics: false, }); return useMemo(() => { diff --git a/static/app/views/explore/spans/spansTab.spec.tsx b/static/app/views/explore/spans/spansTab.spec.tsx new file mode 100644 index 00000000000000..966e9336187fca --- /dev/null +++ b/static/app/views/explore/spans/spansTab.spec.tsx @@ -0,0 +1,117 @@ +import {initializeOrg} from 'sentry-test/initializeOrg'; +import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary'; + +import PageFiltersStore from 'sentry/stores/pageFiltersStore'; +import {trackAnalytics} from 'sentry/utils/analytics'; +import {SpansTabContent} from 'sentry/views/explore/spans/spansTab'; + +jest.mock('sentry/utils/analytics'); + +describe('SpansTabContent', function () { + const {organization, project, router} = initializeOrg({ + organization: { + features: ['visibility-explore-rpc'], + }, + }); + + beforeEach(function () { + // without this the `CompactSelect` component errors with a bunch of async updates + jest.spyOn(console, 'error').mockImplementation(); + + PageFiltersStore.init(); + PageFiltersStore.onInitializeUrlState( + { + projects: [project].map(p => parseInt(p.id, 10)), + environments: [], + datetime: { + period: '7d', + start: null, + end: null, + utc: null, + }, + }, + new Set() + ); + MockApiClient.addMockResponse({ + url: `/subscriptions/${organization.slug}/`, + method: 'GET', + body: {}, + }); + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/recent-searches/`, + method: 'GET', + body: {}, + }); + + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/spans/fields/`, + method: 'GET', + body: [], + }); + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/events/`, + method: 'GET', + body: {}, + }); + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/events-stats/`, + method: 'GET', + body: {}, + }); + MockApiClient.addMockResponse({ + url: `/organizations/${organization.slug}/traces/`, + method: 'GET', + body: {}, + }); + }); + + it('should fire analytics once per change', async function () { + render( + , + {disableRouterMocks: true, router, organization} + ); + + await screen.findByText(/No spans found/); + expect(trackAnalytics).toHaveBeenCalledTimes(1); + expect(trackAnalytics).toHaveBeenCalledWith( + 'trace.explorer.metadata', + expect.objectContaining({ + result_mode: 'span samples', + }) + ); + + (trackAnalytics as jest.Mock).mockClear(); + await userEvent.click(await screen.findByText('Trace Samples')); + + await screen.findByText(/No trace results found/); + expect(trackAnalytics).toHaveBeenCalledTimes(1); + expect(trackAnalytics).toHaveBeenCalledWith( + 'trace.explorer.metadata', + expect.objectContaining({ + result_mode: 'trace samples', + }) + ); + + (trackAnalytics as jest.Mock).mockClear(); + await userEvent.click( + within(screen.getByTestId('section-mode')).getByRole('radio', {name: 'Aggregates'}) + ); + + await screen.findByText(/No spans found/); + expect(trackAnalytics).toHaveBeenCalledTimes(1); + expect(trackAnalytics).toHaveBeenCalledWith( + 'trace.explorer.metadata', + expect.objectContaining({ + result_mode: 'aggregates', + }) + ); + }); +}); diff --git a/static/app/views/explore/spans/spansTab.tsx b/static/app/views/explore/spans/spansTab.tsx index 27f3211b97ae29..fd12f0cbcb3454 100644 --- a/static/app/views/explore/spans/spansTab.tsx +++ b/static/app/views/explore/spans/spansTab.tsx @@ -37,6 +37,7 @@ import { SpanTagsProvider, useSpanTags, } from 'sentry/views/explore/contexts/spanTagsContext'; +import {useAnalytics} from 'sentry/views/explore/hooks/useAnalytics'; import {useExploreAggregatesTable} from 'sentry/views/explore/hooks/useExploreAggregatesTable'; import {useExploreSpansTable} from 'sentry/views/explore/hooks/useExploreSpansTable'; import {useExploreTimeseries} from 'sentry/views/explore/hooks/useExploreTimeseries'; @@ -122,6 +123,14 @@ export function SpansTabContentImpl({ : spansTableResult.result.error?.message ?? ''; const chartError = timeseriesResult.error?.message ?? ''; + useAnalytics({ + queryType, + aggregatesTableResult, + spansTableResult, + tracesTableResult, + timeseriesResult, + }); + return ( diff --git a/static/app/views/explore/tables/aggregatesTable.tsx b/static/app/views/explore/tables/aggregatesTable.tsx index 5e2d2830f6d73b..b257ade8f54630 100644 --- a/static/app/views/explore/tables/aggregatesTable.tsx +++ b/static/app/views/explore/tables/aggregatesTable.tsx @@ -12,7 +12,6 @@ import {IconArrow} from 'sentry/icons/iconArrow'; import {IconStack} from 'sentry/icons/iconStack'; import {IconWarning} from 'sentry/icons/iconWarning'; import {t} from 'sentry/locale'; -import type {Confidence} from 'sentry/types/organization'; import {defined} from 'sentry/utils'; import { fieldAlignment, @@ -20,7 +19,6 @@ import { prettifyParsedFunction, } from 'sentry/utils/discover/fields'; import {useLocation} from 'sentry/utils/useLocation'; -import useOrganization from 'sentry/utils/useOrganization'; import useProjects from 'sentry/utils/useProjects'; import { Table, @@ -34,16 +32,12 @@ import { useTableStyles, } from 'sentry/views/explore/components/table'; import { - useExploreDataset, useExploreGroupBys, useExploreQuery, useExploreSortBys, - useExploreTitle, - useExploreVisualizes, useSetExploreSortBys, } from 'sentry/views/explore/contexts/pageParamsContext'; import {useSpanTags} from 'sentry/views/explore/contexts/spanTagsContext'; -import {useAnalytics} from 'sentry/views/explore/hooks/useAnalytics'; import type {AggregatesTableResult} from 'sentry/views/explore/hooks/useExploreAggregatesTable'; import {TOP_EVENTS_LIMIT, useTopEvents} from 'sentry/views/explore/hooks/useTopEvents'; import {viewSamplesTarget} from 'sentry/views/explore/utils'; @@ -52,22 +46,14 @@ import {FieldRenderer} from './fieldRenderer'; interface AggregatesTableProps { aggregatesTableResult: AggregatesTableResult; - confidences: Confidence[]; } -export function AggregatesTable({ - aggregatesTableResult, - confidences, -}: AggregatesTableProps) { +export function AggregatesTable({aggregatesTableResult}: AggregatesTableProps) { const location = useLocation(); - const organization = useOrganization(); const {projects} = useProjects(); const topEvents = useTopEvents(); - const title = useExploreTitle(); - const dataset = useExploreDataset(); const groupBys = useExploreGroupBys(); - const visualizes = useExploreVisualizes(); const {result, eventView, fields} = aggregatesTableResult; @@ -77,19 +63,6 @@ export function AggregatesTable({ const columns = useMemo(() => eventView.getColumns(), [eventView]); - useAnalytics({ - dataset, - resultLength: result.data?.length, - resultMode: 'aggregates', - resultStatus: result.status, - visualizes, - organization, - columns: groupBys, - userQuery: query, - confidences, - title, - }); - const tableRef = useRef(null); const {initialTableStyles, onResizeMouseDown} = useTableStyles(fields, tableRef, { minimumColumnWidth: 50, diff --git a/static/app/views/explore/tables/spansTable.tsx b/static/app/views/explore/tables/spansTable.tsx index d9d7e8bb22ec98..cd508b74b42c4d 100644 --- a/static/app/views/explore/tables/spansTable.tsx +++ b/static/app/views/explore/tables/spansTable.tsx @@ -8,10 +8,8 @@ import {Tooltip} from 'sentry/components/tooltip'; import {IconArrow} from 'sentry/icons/iconArrow'; import {IconWarning} from 'sentry/icons/iconWarning'; import {t} from 'sentry/locale'; -import type {Confidence} from 'sentry/types/organization'; import {defined} from 'sentry/utils'; import {fieldAlignment, prettifyTagKey} from 'sentry/utils/discover/fields'; -import useOrganization from 'sentry/utils/useOrganization'; import { Table, TableBody, @@ -24,34 +22,23 @@ import { useTableStyles, } from 'sentry/views/explore/components/table'; import { - useExploreDataset, useExploreFields, - useExploreQuery, useExploreSortBys, - useExploreTitle, - useExploreVisualizes, useSetExploreSortBys, } from 'sentry/views/explore/contexts/pageParamsContext'; import {useSpanTags} from 'sentry/views/explore/contexts/spanTagsContext'; -import {useAnalytics} from 'sentry/views/explore/hooks/useAnalytics'; import type {SpansTableResult} from 'sentry/views/explore/hooks/useExploreSpansTable'; import {FieldRenderer} from './fieldRenderer'; interface SpansTableProps { - confidences: Confidence[]; spansTableResult: SpansTableResult; } -export function SpansTable({confidences, spansTableResult}: SpansTableProps) { - const dataset = useExploreDataset(); - const title = useExploreTitle(); +export function SpansTable({spansTableResult}: SpansTableProps) { const fields = useExploreFields(); const sortBys = useExploreSortBys(); const setSortBys = useSetExploreSortBys(); - const query = useExploreQuery(); - const visualizes = useExploreVisualizes(); - const organization = useOrganization(); const visibleFields = useMemo( () => (fields.includes('id') ? fields : ['id', ...fields]), @@ -62,19 +49,6 @@ export function SpansTable({confidences, spansTableResult}: SpansTableProps) { const columnsFromEventView = useMemo(() => eventView.getColumns(), [eventView]); - useAnalytics({ - dataset, - resultLength: result.data?.length, - resultMode: 'span samples', - resultStatus: result.status, - visualizes, - organization, - columns: fields, - userQuery: query, - confidences, - title, - }); - const tableRef = useRef(null); const {initialTableStyles, onResizeMouseDown} = useTableStyles( visibleFields, diff --git a/static/app/views/explore/tables/tracesTable/index.tsx b/static/app/views/explore/tables/tracesTable/index.tsx index 9973fc05795edd..c28c714895eece 100644 --- a/static/app/views/explore/tables/tracesTable/index.tsx +++ b/static/app/views/explore/tables/tracesTable/index.tsx @@ -15,20 +15,13 @@ import {IconChevron} from 'sentry/icons/iconChevron'; import {IconWarning} from 'sentry/icons/iconWarning'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; -import type {Confidence} from 'sentry/types/organization'; import {defined} from 'sentry/utils'; import {trackAnalytics} from 'sentry/utils/analytics'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import useProjects from 'sentry/utils/useProjects'; -import { - useExploreDataset, - useExploreQuery, - useExploreTitle, - useExploreVisualizes, -} from 'sentry/views/explore/contexts/pageParamsContext'; -import {useAnalytics} from 'sentry/views/explore/hooks/useAnalytics'; +import {useExploreQuery} from 'sentry/views/explore/contexts/pageParamsContext'; import type {TracesTableResult} from 'sentry/views/explore/hooks/useExploreTracesTable'; import type {TraceResult} from 'sentry/views/explore/hooks/useTraces'; import { @@ -52,40 +45,14 @@ import { } from 'sentry/views/explore/tables/tracesTable/styles'; interface TracesTableProps { - confidences: Confidence[]; tracesTableResult: TracesTableResult; } -export function TracesTable({confidences, tracesTableResult}: TracesTableProps) { - const title = useExploreTitle(); - const dataset = useExploreDataset(); +export function TracesTable({tracesTableResult}: TracesTableProps) { const query = useExploreQuery(); - const visualizes = useExploreVisualizes(); - const organization = useOrganization(); const {result} = tracesTableResult; - useAnalytics({ - dataset, - resultLength: result.data?.data?.length, - resultMode: 'trace samples', - resultStatus: result.status, - resultMissingRoot: result.data?.data?.filter(trace => !defined(trace.name))?.length, - visualizes, - organization, - columns: [ - 'trace id', - 'trace root', - 'total spans', - 'timeline', - 'root duration', - 'timestamp', - ], - userQuery: query, - confidences, - title, - }); - const {data, isPending, isError, getResponseHeader} = result; const showErrorState = !isPending && isError; diff --git a/static/app/views/insights/common/queries/useSortedTimeSeries.tsx b/static/app/views/insights/common/queries/useSortedTimeSeries.tsx index 33feae8519fd64..d7a74157531f07 100644 --- a/static/app/views/insights/common/queries/useSortedTimeSeries.tsx +++ b/static/app/views/insights/common/queries/useSortedTimeSeries.tsx @@ -1,3 +1,5 @@ +import {useMemo} from 'react'; + import type {Series} from 'sentry/types/echarts'; import type { Confidence, @@ -109,9 +111,9 @@ export const useSortedTimeSeries = < const isFetchingOrLoading = result.isPending || result.isFetching; - const data: SeriesMap = isFetchingOrLoading - ? {} - : transformToSeriesMap(result.data, yAxis); + const data: SeriesMap = useMemo(() => { + return isFetchingOrLoading ? {} : transformToSeriesMap(result.data, yAxis); + }, [isFetchingOrLoading, result.data, yAxis]); const pageLinks = result.response?.getResponseHeader('Link') ?? undefined; diff --git a/static/app/views/insights/common/queries/useSpansQuery.tsx b/static/app/views/insights/common/queries/useSpansQuery.tsx index d197f1f04108a6..c4782c48d13c92 100644 --- a/static/app/views/insights/common/queries/useSpansQuery.tsx +++ b/static/app/views/insights/common/queries/useSpansQuery.tsx @@ -26,6 +26,7 @@ export function useSpansQuery({ referrer = 'use-spans-query', allowAggregateConditions, cursor, + trackResponseAnalytics = true, }: { allowAggregateConditions?: boolean; cursor?: string; @@ -34,6 +35,7 @@ export function useSpansQuery({ initialData?: T; limit?: number; referrer?: string; + trackResponseAnalytics?: boolean; }) { const isTimeseriesQuery = (eventView?.yAxis?.length ?? 0) > 0; const queryFunction = isTimeseriesQuery @@ -55,7 +57,9 @@ export function useSpansQuery({ allowAggregateConditions, }); - TrackResponse(eventView, response); + if (trackResponseAnalytics) { + TrackResponse(eventView, response); + } return response; }