@@ -9,6 +9,7 @@ import type {Sort} from 'sentry/utils/discover/fields';
99import { DiscoverDatasets } from 'sentry/utils/discover/types' ;
1010import { MutableSearch } from 'sentry/utils/tokenizeSearch' ;
1111import useOrganization from 'sentry/utils/useOrganization' ;
12+ import usePageFilters from 'sentry/utils/usePageFilters' ;
1213import type { TimeSeries } from 'sentry/views/dashboards/widgets/common/types' ;
1314import { useLogsAutoRefreshEnabled } from 'sentry/views/explore/contexts/logs/logsAutoRefreshContext' ;
1415import { Mode } from 'sentry/views/explore/contexts/pageParamsContext/mode' ;
@@ -19,6 +20,8 @@ import type {TracesTableResult} from 'sentry/views/explore/hooks/useExploreTrace
1920import { useTopEvents } from 'sentry/views/explore/hooks/useTopEvents' ;
2021import { type useLogsAggregatesTable } from 'sentry/views/explore/logs/useLogsAggregatesTable' ;
2122import type { UseInfiniteLogsQueryResult } from 'sentry/views/explore/logs/useLogsQuery' ;
23+ import { useMetricAggregatesTable } from 'sentry/views/explore/metrics/hooks/useMetricAggregatesTable' ;
24+ import { useMetricSamplesTable } from 'sentry/views/explore/metrics/hooks/useMetricSamplesTable' ;
2225import type { ReadableExploreQueryParts } from 'sentry/views/explore/multiQueryMode/locationUtils' ;
2326import {
2427 useQueryParamsFields ,
@@ -27,6 +30,7 @@ import {
2730 useQueryParamsTitle ,
2831 useQueryParamsVisualizes ,
2932} from 'sentry/views/explore/queryParams/context' ;
33+ import type { ReadableQueryParams } from 'sentry/views/explore/queryParams/readableQueryParams' ;
3034import { Visualize } from 'sentry/views/explore/queryParams/visualize' ;
3135import { useSpansDataset } from 'sentry/views/explore/spans/spansQueryParams' ;
3236import {
@@ -685,6 +689,260 @@ function computeEmptyBuckets(
685689 } ) ;
686690}
687691
692+ export function useMetricsPanelAnalytics ( {
693+ interval,
694+ isTopN,
695+ metricAggregatesTableResult,
696+ metricSamplesTableResult,
697+ metricTimeseriesResult,
698+ mode,
699+ yAxis,
700+ sortBys,
701+ aggregateSortBys,
702+ } : {
703+ aggregateSortBys : readonly Sort [ ] ;
704+ interval : string ;
705+ isTopN : boolean ;
706+ metricAggregatesTableResult : ReturnType < typeof useMetricAggregatesTable > ;
707+ metricSamplesTableResult : ReturnType < typeof useMetricSamplesTable > ;
708+ metricTimeseriesResult : ReturnType < typeof useSortedTimeSeries > ;
709+ mode : Mode ;
710+ sortBys : readonly Sort [ ] ;
711+ yAxis : string ;
712+ } ) {
713+ const organization = useOrganization ( ) ;
714+
715+ const dataset = DiscoverDatasets . METRICS ;
716+ const dataScanned = metricSamplesTableResult . result . meta ?. dataScanned ?? '' ;
717+ const search = useQueryParamsSearch ( ) ;
718+ const query = useQueryParamsQuery ( ) ;
719+ const fields = useQueryParamsFields ( ) ;
720+
721+ const tableError =
722+ mode === Mode . AGGREGATE
723+ ? ( metricAggregatesTableResult . result . error ?. message ?? '' )
724+ : ( metricSamplesTableResult . error ?. message ?? '' ) ;
725+ const query_status = tableError ? 'error' : 'success' ;
726+
727+ const aggregatesResultLengthBox = useBox (
728+ metricAggregatesTableResult . result . data ?. length || 0
729+ ) ;
730+ const resultLengthBox = useBox ( metricSamplesTableResult . result . data ?. length || 0 ) ;
731+ const fieldsBox = useBox ( fields ) ;
732+ const yAxesBox = useBox ( [ yAxis ] ) ;
733+ const sortBysBox = useBox ( sortBys . map ( formatSort ) ) ;
734+ const aggregateSortBysBox = useBox ( aggregateSortBys . map ( formatSort ) ) ;
735+
736+ const timeseriesData = useBox ( metricTimeseriesResult . data ) ;
737+
738+ useEffect ( ( ) => {
739+ if ( mode !== Mode . SAMPLES ) {
740+ return ;
741+ }
742+
743+ if ( metricSamplesTableResult . result . isFetching ) {
744+ return ;
745+ }
746+
747+ trackAnalytics ( 'metrics.explorer.panel.metadata' , {
748+ organization,
749+ dataset,
750+ dataScanned,
751+ columns : fieldsBox . current ,
752+ columns_count : fieldsBox . current . length ,
753+ confidences : computeConfidence ( yAxesBox . current , timeseriesData . current ) ,
754+ empty_buckets_percentage : computeEmptyBuckets (
755+ yAxesBox . current ,
756+ timeseriesData . current
757+ ) ,
758+ interval,
759+ query_status,
760+ sample_counts : computeVisualizeSampleTotals (
761+ yAxesBox . current ,
762+ timeseriesData . current ,
763+ isTopN
764+ ) ,
765+ table_result_length : resultLengthBox . current ,
766+ table_result_missing_root : 0 ,
767+ table_result_mode : 'metric samples' ,
768+ table_result_sort : sortBysBox . current ,
769+ user_queries : search . formatString ( ) ,
770+ user_queries_count : search . tokens . length ,
771+ } ) ;
772+
773+ info (
774+ fmt `metric.explorer.panel.metadata:
775+ organization: ${ organization . slug }
776+ dataScanned: ${ dataScanned }
777+ dataset: ${ dataset }
778+ query: ${ query }
779+ fields: ${ fieldsBox . current }
780+ query_status: ${ query_status }
781+ result_length: ${ String ( resultLengthBox . current ) }
782+ user_queries: ${ search . formatString ( ) }
783+ user_queries_count: ${ String ( search . tokens . length ) }
784+ ` ,
785+ { isAnalytics : true }
786+ ) ;
787+ } , [
788+ organization ,
789+ dataset ,
790+ dataScanned ,
791+ query ,
792+ fieldsBox ,
793+ interval ,
794+ query_status ,
795+ isTopN ,
796+ metricSamplesTableResult . result . isFetching ,
797+ search ,
798+ timeseriesData ,
799+ mode ,
800+ resultLengthBox ,
801+ sortBysBox ,
802+ yAxesBox ,
803+ ] ) ;
804+
805+ useEffect ( ( ) => {
806+ if ( mode !== Mode . AGGREGATE ) {
807+ return ;
808+ }
809+
810+ if ( metricAggregatesTableResult . result . isPending ) {
811+ return ;
812+ }
813+
814+ trackAnalytics ( 'metrics.explorer.panel.metadata' , {
815+ organization,
816+ dataset,
817+ dataScanned,
818+ columns : fieldsBox . current ,
819+ columns_count : fieldsBox . current . length ,
820+ confidences : computeConfidence ( [ yAxis ] , timeseriesData . current ) ,
821+ empty_buckets_percentage : computeEmptyBuckets ( [ yAxis ] , timeseriesData . current ) ,
822+ interval,
823+ query_status,
824+ sample_counts : computeVisualizeSampleTotals (
825+ [ yAxis ] ,
826+ timeseriesData . current ,
827+ isTopN
828+ ) ,
829+ table_result_length : aggregatesResultLengthBox . current ,
830+ table_result_missing_root : 0 ,
831+ table_result_mode : 'aggregates' ,
832+ table_result_sort : aggregateSortBysBox . current ,
833+ user_queries : search . formatString ( ) ,
834+ user_queries_count : search . tokens . length ,
835+ } ) ;
836+
837+ info (
838+ fmt `metric.explorer.panel.metadata:
839+ organization: ${ organization . slug }
840+ dataScanned: ${ dataScanned }
841+ dataset: ${ dataset }
842+ query: ${ query }
843+ fields: ${ fieldsBox . current }
844+ query_status: ${ query_status }
845+ result_length: ${ String ( aggregatesResultLengthBox . current ) }
846+ user_queries: ${ search . formatString ( ) }
847+ user_queries_count: ${ String ( search . tokens . length ) }
848+ ` ,
849+ { isAnalytics : true }
850+ ) ;
851+ } , [
852+ aggregateSortBysBox ,
853+ aggregatesResultLengthBox ,
854+ dataScanned ,
855+ dataset ,
856+ fieldsBox ,
857+ interval ,
858+ isTopN ,
859+ metricAggregatesTableResult . result . isPending ,
860+ timeseriesData ,
861+ mode ,
862+ organization ,
863+ query ,
864+ query_status ,
865+ search ,
866+ yAxis ,
867+ ] ) ;
868+ }
869+
870+ export function useMetricsAnalytics ( {
871+ interval,
872+ metricQueries,
873+ } : {
874+ interval : string ;
875+ metricQueries : Array < { queryParams : ReadableQueryParams } > ;
876+ } ) {
877+ const organization = useOrganization ( ) ;
878+ const { selection} = usePageFilters ( ) ;
879+
880+ const {
881+ data : { hasExceededPerformanceUsageLimit} ,
882+ isLoading : isLoadingSubscriptionDetails ,
883+ } = usePerformanceSubscriptionDetails ( { traceItemDataset : 'default' } ) ;
884+
885+ const metricQueriesCount = useBox ( metricQueries . length ) ;
886+ const metricPanelsWithGroupBysCount = useBox (
887+ metricQueries . filter ( mq =>
888+ mq . queryParams . groupBys . some ( ( gb : string ) => gb . trim ( ) . length > 0 )
889+ ) . length
890+ ) ;
891+ const metricPanelsWithFiltersCount = useBox (
892+ metricQueries . filter ( mq => mq . queryParams . query . trim ( ) . length > 0 ) . length
893+ ) ;
894+
895+ useEffect ( ( ) => {
896+ if ( isLoadingSubscriptionDetails ) {
897+ return ;
898+ }
899+
900+ const datetimeSelection = `${ selection . datetime . start || '' } -${ selection . datetime . end || '' } -${ selection . datetime . period || '' } ` ;
901+ const projectCount = selection . projects . length ;
902+ const environmentCount = selection . environments . length ;
903+
904+ trackAnalytics ( 'metrics.explorer.metadata' , {
905+ organization,
906+ datetime_selection : datetimeSelection ,
907+ environment_count : environmentCount ,
908+ has_exceeded_performance_usage_limit : hasExceededPerformanceUsageLimit ,
909+ interval,
910+ metric_panels_with_filters_count : metricPanelsWithFiltersCount . current ,
911+ metric_panels_with_group_bys_count : metricPanelsWithGroupBysCount . current ,
912+ metric_queries_count : metricQueriesCount . current ,
913+ project_count : projectCount ,
914+ } ) ;
915+
916+ info (
917+ fmt `metrics.explorer.metadata:
918+ organization: ${ organization . slug }
919+ datetime_selection: ${ datetimeSelection }
920+ environment_count: ${ String ( environmentCount ) }
921+ interval: ${ interval }
922+ metric_queries_count: ${ String ( metricQueriesCount . current ) }
923+ metric_panels_with_group_bys_count: ${ String ( metricPanelsWithGroupBysCount . current ) }
924+ metric_panels_with_filters_count: ${ String ( metricPanelsWithFiltersCount . current ) }
925+ project_count: ${ String ( projectCount ) }
926+ has_exceeded_performance_usage_limit: ${ String ( hasExceededPerformanceUsageLimit ) }
927+ ` ,
928+ { isAnalytics : true }
929+ ) ;
930+ } , [
931+ hasExceededPerformanceUsageLimit ,
932+ interval ,
933+ isLoadingSubscriptionDetails ,
934+ metricQueriesCount ,
935+ metricPanelsWithGroupBysCount ,
936+ metricPanelsWithFiltersCount ,
937+ organization ,
938+ selection . datetime . end ,
939+ selection . datetime . period ,
940+ selection . datetime . start ,
941+ selection . environments . length ,
942+ selection . projects . length ,
943+ ] ) ;
944+ }
945+
688946function useBox < T > ( value : T ) : RefObject < T > {
689947 const box = useRef ( value ) ;
690948 box . current = value ;
0 commit comments