diff --git a/static/app/views/explore/components/attributeDetails.tsx b/static/app/views/explore/components/attributeDetails.tsx index 49cf7ff70be410..6869f1ec534032 100644 --- a/static/app/views/explore/components/attributeDetails.tsx +++ b/static/app/views/explore/components/attributeDetails.tsx @@ -31,7 +31,9 @@ export function AttributeDetails({ ); } -function traceItemTypeToType(traceItemType: TraceItemDataset): 'span' | 'log' { +function traceItemTypeToType( + traceItemType: TraceItemDataset +): 'span' | 'log' | 'tracemetric' { if (traceItemType === TraceItemDataset.SPANS) { return 'span' as const; } @@ -40,6 +42,10 @@ function traceItemTypeToType(traceItemType: TraceItemDataset): 'span' | 'log' { return 'log' as const; } + if (traceItemType === TraceItemDataset.TRACEMETRICS) { + return 'tracemetric' as const; + } + throw new Error('Cannot convert unknown trace item type to type'); } diff --git a/static/app/views/explore/hooks/useGetTraceItemAttributeKeys.tsx b/static/app/views/explore/hooks/useGetTraceItemAttributeKeys.tsx index 1ce7ecdb492e99..116645f2dbae2d 100644 --- a/static/app/views/explore/hooks/useGetTraceItemAttributeKeys.tsx +++ b/static/app/views/explore/hooks/useGetTraceItemAttributeKeys.tsx @@ -14,6 +14,7 @@ import type { interface UseGetTraceItemAttributeKeysProps extends UseTraceItemAttributeBaseProps { projectIds?: Array; + query?: string; } type TraceItemAttributeKeyOptions = Pick< @@ -23,6 +24,7 @@ type TraceItemAttributeKeyOptions = Pick< attributeType: 'string' | 'number'; itemType: TraceItemDataset; project?: string[]; + query?: string; substringMatch?: string; }; @@ -32,17 +34,20 @@ export function makeTraceItemAttributeKeysQueryOptions({ datetime, projectIds, search, + query, }: { datetime: PageFilters['datetime']; traceItemType: TraceItemDataset; type: 'string' | 'number'; projectIds?: Array; + query?: string; search?: string; }): TraceItemAttributeKeyOptions { const options: TraceItemAttributeKeyOptions = { itemType: traceItemType, attributeType: type, project: projectIds?.map(String), + query, substringMatch: search, ...normalizeDateTimeParams(datetime), }; @@ -56,6 +61,7 @@ export function useGetTraceItemAttributeKeys({ traceItemType, projectIds, type, + query, }: UseGetTraceItemAttributeKeysProps) { const api = useApi(); const organization = useOrganization(); @@ -69,6 +75,7 @@ export function useGetTraceItemAttributeKeys({ datetime: selection.datetime, projectIds: projectIds ?? selection.projects, search: queryString, + query, }); let result: Tag[]; @@ -87,7 +94,7 @@ export function useGetTraceItemAttributeKeys({ return getTraceItemTagCollection(result, type); }, - [api, organization, selection, traceItemType, projectIds, type] + [api, organization, selection, traceItemType, projectIds, type, query] ); return getTraceItemAttributeKeys; diff --git a/static/app/views/explore/hooks/useTraceItemAttributeKeys.tsx b/static/app/views/explore/hooks/useTraceItemAttributeKeys.tsx index dbc09bfd5e2518..e72d26cadbbb00 100644 --- a/static/app/views/explore/hooks/useTraceItemAttributeKeys.tsx +++ b/static/app/views/explore/hooks/useTraceItemAttributeKeys.tsx @@ -13,6 +13,7 @@ import type {UseTraceItemAttributeBaseProps} from 'sentry/views/explore/types'; interface UseTraceItemAttributeKeysProps extends UseTraceItemAttributeBaseProps { enabled?: boolean; + query?: string; } export function useTraceItemAttributeKeys({ @@ -20,6 +21,7 @@ export function useTraceItemAttributeKeys({ type, traceItemType, projects, + query, }: UseTraceItemAttributeKeysProps) { const {selection} = usePageFilters(); @@ -33,8 +35,9 @@ export function useTraceItemAttributeKeys({ type, datetime: selection.datetime, projectIds, + query, }); - }, [selection, traceItemType, type, projectIds]); + }, [selection, traceItemType, type, projectIds, query]); const queryKey = useMemo( () => ['use-trace-item-attribute-keys', queryOptions], @@ -45,6 +48,7 @@ export function useTraceItemAttributeKeys({ traceItemType, type, projectIds, + query, }); const {data, isFetching, error} = useQuery({ diff --git a/static/app/views/explore/metrics/metricPanel.tsx b/static/app/views/explore/metrics/metricPanel.tsx index ddf3cd2d7ffffb..791bb7db29f51d 100644 --- a/static/app/views/explore/metrics/metricPanel.tsx +++ b/static/app/views/explore/metrics/metricPanel.tsx @@ -8,11 +8,13 @@ import {DiscoverDatasets} from 'sentry/utils/discover/types'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; import {useDimensions} from 'sentry/utils/useDimensions'; import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval'; +import {useTopEvents} from 'sentry/views/explore/hooks/useTopEvents'; import {MetricsGraph} from 'sentry/views/explore/metrics/metricGraph'; import MetricInfoTabs from 'sentry/views/explore/metrics/metricInfoTabs'; import {type TraceMetric} from 'sentry/views/explore/metrics/metricQuery'; import {MetricRow} from 'sentry/views/explore/metrics/metricRow'; import {useMetricVisualize} from 'sentry/views/explore/metrics/metricsQueryParams'; +import {useQueryParamsGroupBys} from 'sentry/views/explore/queryParams/context'; import {useSortedTimeSeries} from 'sentry/views/insights/common/queries/useSortedTimeSeries'; interface MetricPanelProps { @@ -24,17 +26,20 @@ const MIN_RIGHT_WIDTH = 400; export function MetricPanel({traceMetric}: MetricPanelProps) { const visualize = useMetricVisualize(); + const groupBys = useQueryParamsGroupBys(); const measureRef = useRef(null); const {width} = useDimensions({elementRef: measureRef}); const [interval] = useChartInterval(); + const topEvents = useTopEvents(); const timeseriesResult = useSortedTimeSeries( { search: new MutableSearch(`metric.name:${traceMetric.name}`), yAxis: [visualize.yAxis], interval, - fields: [], + fields: [...groupBys], enabled: Boolean(traceMetric.name), + topEvents, }, 'api.explore.metrics-stats', DiscoverDatasets.TRACEMETRICS diff --git a/static/app/views/explore/metrics/metricRow/groupBySelector.tsx b/static/app/views/explore/metrics/metricRow/groupBySelector.tsx new file mode 100644 index 00000000000000..0342f1bb91db65 --- /dev/null +++ b/static/app/views/explore/metrics/metricRow/groupBySelector.tsx @@ -0,0 +1,72 @@ +import {CompactSelect} from 'sentry/components/core/compactSelect'; +import type {SelectOption} from 'sentry/components/core/compactSelect/types'; +import LoadingIndicator from 'sentry/components/loadingIndicator'; +import {MutableSearch} from 'sentry/components/searchSyntax/mutableSearch'; +import {useGroupByFields} from 'sentry/views/explore/hooks/useGroupByFields'; +import {useTraceItemAttributeKeys} from 'sentry/views/explore/hooks/useTraceItemAttributeKeys'; +import { + useQueryParamsGroupBys, + useSetQueryParamsGroupBys, +} from 'sentry/views/explore/queryParams/context'; +import {TraceItemDataset} from 'sentry/views/explore/types'; + +interface GroupBySelectorProps { + /** + * The metric name to filter attributes by + */ + metricName: string; +} + +/** + * A selector component for choosing metric group by attributes. + * Fetches available attribute keys from the trace-items API endpoint + * and displays them as options in a compact select dropdown. + */ +export function GroupBySelector({metricName}: GroupBySelectorProps) { + const groupBys = useQueryParamsGroupBys(); + const setGroupBys = useSetQueryParamsGroupBys(); + + const metricNameFilter = metricName + ? MutableSearch.fromQueryObject({['metric.name']: [metricName]}).formatString() + : undefined; + + const {attributes: numberTags, isLoading: numberTagsLoading} = + useTraceItemAttributeKeys({ + traceItemType: TraceItemDataset.TRACEMETRICS, + type: 'number', + enabled: Boolean(metricNameFilter), + query: metricNameFilter, + }); + const {attributes: stringTags, isLoading: stringTagsLoading} = + useTraceItemAttributeKeys({ + traceItemType: TraceItemDataset.TRACEMETRICS, + type: 'string', + enabled: Boolean(metricNameFilter), + query: metricNameFilter, + }); + + const enabledOptions: Array> = useGroupByFields({ + groupBys: [], + numberTags: numberTags ?? {}, + stringTags: stringTags ?? {}, + traceItemType: TraceItemDataset.TRACEMETRICS, + hideEmptyOption: true, + }); + + const isLoading = numberTagsLoading || stringTagsLoading; + const triggerLabel = isLoading ? : undefined; + + return ( + + multiple + searchable + options={enabledOptions} + value={[...groupBys]} + disabled={isLoading || enabledOptions.length === 0} + triggerProps={triggerLabel ? {children: triggerLabel} : undefined} + onChange={selectedOptions => { + setGroupBys(selectedOptions.map(option => option.value)); + }} + /> + ); +} diff --git a/static/app/views/explore/metrics/metricRow/index.tsx b/static/app/views/explore/metrics/metricRow/index.tsx index 50b0be8a7ccc49..b821bff230e051 100644 --- a/static/app/views/explore/metrics/metricRow/index.tsx +++ b/static/app/views/explore/metrics/metricRow/index.tsx @@ -12,12 +12,9 @@ import { import {useMetricOptions} from 'sentry/views/explore/hooks/useMetricOptions'; import {type TraceMetric} from 'sentry/views/explore/metrics/metricQuery'; import {AggregateDropdown} from 'sentry/views/explore/metrics/metricRow/aggregateDropdown'; +import {GroupBySelector} from 'sentry/views/explore/metrics/metricRow/groupBySelector'; +import {useSetMetricName} from 'sentry/views/explore/metrics/metricsQueryParams'; import { - useMetricVisualize, - useSetMetricName, -} from 'sentry/views/explore/metrics/metricsQueryParams'; -import { - useQueryParamsGroupBys, useQueryParamsQuery, useSetQueryParamsQuery, } from 'sentry/views/explore/queryParams/context'; @@ -67,9 +64,6 @@ function MetricToolbar({ tracesItemSearchQueryBuilderProps, traceMetric, }: MetricToolbarProps) { - const visualize = useMetricVisualize(); - const groupBys = useQueryParamsGroupBys(); - const query = useQueryParamsQuery(); const {data: metricOptionsData} = useMetricOptions(); const setMetricName = useSetMetricName(); @@ -80,18 +74,6 @@ function MetricToolbar({ value: option['metric.name'], type: option['metric.type'], })) ?? []), - // TODO(nar): Remove these when we actually have metrics served - // This is only used for providing an option to test current selection behavior - { - label: 'test-distribution', - value: 'test-distribution', - type: 'distribution' as const, - }, - { - label: 'test-gauge', - value: 'test-gauge', - type: 'gauge' as const, - }, ]; }, [metricOptionsData]); @@ -103,7 +85,6 @@ function MetricToolbar({ return (
- {traceMetric.name}/{visualize.yAxis}/ by {groupBys.join(',')}/ where {query} {t('Query')} {t('by')} - + {t('where')}