Skip to content

Commit

Permalink
fix(explore): Lift explore analytics and ensure it fires only once (#…
Browse files Browse the repository at this point in the history
…83992)

The analytics were firing more than once per load. This lifts it into
the top level component and ensure it fires exactly once.
  • Loading branch information
Zylphrex authored Jan 24, 2025
1 parent b73a23f commit c35a202
Show file tree
Hide file tree
Showing 12 changed files with 321 additions and 143 deletions.
6 changes: 5 additions & 1 deletion static/app/utils/analytics/tracingEventMap.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {Confidence} from 'sentry/types/organization';
import type {Visualize} from 'sentry/views/explore/contexts/pageParamsContext/visualizes';

export type TracingEventParameters = {
Expand All @@ -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[];
Expand Down
1 change: 1 addition & 0 deletions static/app/views/explore/charts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ export function useExtrapolationMeta({
initialData: [],
referrer: 'api.explore.spans-extrapolation-meta',
enabled: dataset === DiscoverDatasets.SPANS_EAP_RPC,
trackResponseAnalytics: false,
});
}

Expand Down
221 changes: 173 additions & 48 deletions static/app/views/explore/hooks/useAnalytics.tsx
Original file line number Diff line number Diff line change
@@ -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<any, RequestError>['status'];
userQuery: string;
visualizes: Visualize[];
resultMissingRoot?: number;
title?: string;
aggregatesTableResult: AggregatesTableResult;
queryType: 'aggregate' | 'samples' | 'traces';
spansTableResult: SpansTableResult;
timeseriesResult: ReturnType<typeof useSortedTimeSeries>;
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<typeof useSortedTimeSeries>['data']
) {
return visualizes.map(visualize => {
const dedupedYAxes = dedupeArray(visualize.yAxes);
const series = dedupedYAxes.flatMap(yAxis => data[yAxis]).filter(defined);
return combineConfidenceForSeries(series);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export function useExploreAggregatesTable({
initialData: [],
limit,
referrer: 'api.explore.spans-aggregates-table',
trackResponseAnalytics: false,
});

return useMemo(() => {
Expand Down
1 change: 1 addition & 0 deletions static/app/views/explore/hooks/useExploreSpansTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export function useExploreSpansTable({
limit,
referrer: 'api.explore.spans-samples-table',
allowAggregateConditions: false,
trackResponseAnalytics: false,
});

return useMemo(() => {
Expand Down
Loading

0 comments on commit c35a202

Please sign in to comment.