Skip to content

Commit e21d471

Browse files
k-fishpriscilawebdev
authored andcommitted
feat(tracemetrics): Update table implementations and fix metric name (#102737)
### Summary Splits out tables and cleans up some code before infinite table + trace view changes.
1 parent 67390ba commit e21d471

23 files changed

+1153
-534
lines changed

static/app/views/explore/hooks/useMetricOptions.spec.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ describe('useMetricOptions', () => {
5050
it('fetches metric options from tracemetrics dataset', async () => {
5151
const mockData = {
5252
data: [
53+
{['metric.name']: 'metric.c', ['metric.type']: 'counter'},
5354
{['metric.name']: 'metric.a', ['metric.type']: 'distribution'},
5455
{['metric.name']: 'metric.b', ['metric.type']: 'distribution'},
55-
{['metric.name']: 'metric.c', ['metric.type']: 'counter'},
5656
],
5757
};
5858

@@ -63,9 +63,8 @@ describe('useMetricOptions', () => {
6363
match: [
6464
MockApiClient.matchQuery({
6565
dataset: DiscoverDatasets.TRACEMETRICS,
66-
field: ['metric.name', 'metric.type', 'count(metric.name)'],
66+
field: ['metric.name', 'metric.type', 'metric.unit', 'count(metric.name)'],
6767
referrer: 'api.explore.metric-options',
68-
orderby: 'metric.name',
6968
}),
7069
],
7170
});
@@ -86,9 +85,13 @@ describe('useMetricOptions', () => {
8685
);
8786
});
8887

89-
it('sorts metrics alphabetically by name', () => {
88+
it('sorts metrics alphabetically by name', async () => {
9089
const mockData = {
91-
data: [],
90+
data: [
91+
{['metric.name']: 'metric.z', ['metric.type']: 'counter'},
92+
{['metric.name']: 'metric.a', ['metric.type']: 'distribution'},
93+
{['metric.name']: 'metric.m', ['metric.type']: 'gauge'},
94+
],
9295
};
9396

9497
const mockRequest = MockApiClient.addMockResponse({
@@ -97,19 +100,31 @@ describe('useMetricOptions', () => {
97100
body: mockData,
98101
});
99102

100-
renderHookWithProviders(useMetricOptions, {
103+
const {result} = renderHookWithProviders(useMetricOptions, {
101104
...context,
102105
});
103106

107+
await waitFor(() => expect(result.current.isSuccess).toBe(true));
108+
104109
expect(mockRequest).toHaveBeenCalledTimes(1);
105110
expect(mockRequest).toHaveBeenCalledWith(
106111
`/organizations/${organization.slug}/events/`,
107112
expect.objectContaining({
108113
query: expect.objectContaining({
109-
orderby: 'metric.name',
114+
dataset: 'tracemetrics',
115+
field: ['metric.name', 'metric.type', 'metric.unit', 'count(metric.name)'],
116+
referrer: 'api.explore.metric-options',
110117
}),
111118
})
112119
);
120+
121+
await waitFor(() =>
122+
expect(result.current.data?.data).toEqual([
123+
{['metric.name']: 'metric.a', ['metric.type']: 'distribution'},
124+
{['metric.name']: 'metric.m', ['metric.type']: 'gauge'},
125+
{['metric.name']: 'metric.z', ['metric.type']: 'counter'},
126+
])
127+
);
113128
});
114129

115130
it('handles empty response', async () => {
Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,62 @@
1-
import {useMemo} from 'react';
1+
import {useEffect, useMemo} from 'react';
22

33
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
44
import type {PageFilters} from 'sentry/types/core';
55
import {DiscoverDatasets} from 'sentry/utils/discover/types';
66
import {useApiQuery, type ApiQueryKey} from 'sentry/utils/queryClient';
7+
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
78
import useOrganization from 'sentry/utils/useOrganization';
89
import usePageFilters from 'sentry/utils/usePageFilters';
9-
10-
interface EventsMetricResult {
11-
data: Array<{
12-
['metric.name']: string;
13-
['metric.type']: string;
14-
}>;
15-
meta?: {
16-
fields?: Record<string, string>;
17-
};
18-
}
10+
import {
11+
TraceMetricKnownFieldKey,
12+
type TraceMetricEventsResult,
13+
} from 'sentry/views/explore/metrics/types';
1914

2015
interface UseMetricOptionsProps {
2116
datetime?: PageFilters['datetime'];
2217
enabled?: boolean;
18+
orgSlug?: string;
2319
projectIds?: PageFilters['projects'];
20+
search?: string;
2421
}
2522

2623
function metricOptionsQueryKey({
2724
orgSlug,
2825
projectIds,
2926
datetime,
30-
}: {
31-
orgSlug: string;
32-
datetime?: PageFilters['datetime'];
33-
projectIds?: number[];
34-
}): ApiQueryKey {
27+
search,
28+
}: UseMetricOptionsProps = {}): ApiQueryKey {
29+
const searchValue = new MutableSearch('');
30+
if (search) {
31+
searchValue.addStringContainsFilter(
32+
`${TraceMetricKnownFieldKey.METRIC_NAME}:${search}`
33+
);
34+
}
3535
const query: Record<string, string | string[] | number[]> = {
3636
dataset: DiscoverDatasets.TRACEMETRICS,
37-
field: ['metric.name', 'metric.type', 'count(metric.name)'],
37+
field: [
38+
TraceMetricKnownFieldKey.METRIC_NAME,
39+
TraceMetricKnownFieldKey.METRIC_TYPE,
40+
TraceMetricKnownFieldKey.METRIC_UNIT,
41+
`count(${TraceMetricKnownFieldKey.METRIC_NAME})`,
42+
],
43+
query: searchValue.formatString(),
3844
referrer: 'api.explore.metric-options',
39-
orderby: 'metric.name',
4045
};
4146

4247
if (projectIds?.length) {
4348
query.project = projectIds.map(String);
4449
}
4550

46-
if (datetime) {
51+
if (search && datetime) {
52+
// If searching we use the full filters in order to not miss the result.
4753
Object.entries(normalizeDateTimeParams(datetime)).forEach(([key, value]) => {
4854
if (value !== undefined) {
4955
query[key] = value as string | string[];
5056
}
5157
});
58+
} else {
59+
query.statsPeriod = '24h'; // Default to a much smaller time window if not searching.
5260
}
5361

5462
return [`/organizations/${orgSlug}/events/`, {query}];
@@ -59,6 +67,7 @@ function metricOptionsQueryKey({
5967
* This is used to populate metric selection options in the Explore interface.
6068
*/
6169
export function useMetricOptions({
70+
search,
6271
projectIds,
6372
datetime,
6473
enabled = true,
@@ -69,18 +78,39 @@ export function useMetricOptions({
6978
const queryKey = useMemo(
7079
() =>
7180
metricOptionsQueryKey({
81+
search,
7282
orgSlug: organization.slug,
7383
projectIds: projectIds ?? selection.projects,
7484
datetime: datetime ?? selection.datetime,
7585
}),
76-
[organization.slug, projectIds, selection.projects, selection.datetime, datetime]
86+
[
87+
organization.slug,
88+
projectIds,
89+
search,
90+
selection.projects,
91+
selection.datetime,
92+
datetime,
93+
]
7794
);
7895

79-
return useApiQuery<EventsMetricResult>(queryKey, {
96+
const result = useApiQuery<TraceMetricEventsResult>(queryKey, {
8097
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
8198
refetchOnWindowFocus: false,
8299
refetchOnMount: false,
83100
retry: false,
84101
enabled,
85102
});
103+
104+
// This replaces order-by metric.name as that will never be performant over large time periods with high numbers of metrics.
105+
useEffect(() => {
106+
if (result.data?.data) {
107+
result.data.data.sort((a, b) => {
108+
return a[TraceMetricKnownFieldKey.METRIC_NAME].localeCompare(
109+
b[TraceMetricKnownFieldKey.METRIC_NAME]
110+
);
111+
});
112+
}
113+
}, [result.data]);
114+
115+
return result;
86116
}

static/app/views/explore/metrics/constants.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {SelectOption} from 'sentry/components/core/compactSelect';
22
import {
33
TraceMetricKnownFieldKey,
4+
VirtualTableSampleColumnKey,
45
type TraceMetricFieldKey,
56
} from 'sentry/views/explore/metrics/types';
67

@@ -13,6 +14,17 @@ const AlwaysHiddenTraceMetricFields: TraceMetricFieldKey[] = [
1314
TraceMetricKnownFieldKey.OLD_PROJECT_ID,
1415
];
1516

17+
export const AlwaysPresentTraceMetricFields: TraceMetricFieldKey[] = [
18+
TraceMetricKnownFieldKey.ID,
19+
TraceMetricKnownFieldKey.PROJECT_ID,
20+
TraceMetricKnownFieldKey.TRACE,
21+
TraceMetricKnownFieldKey.SPAN_ID,
22+
TraceMetricKnownFieldKey.OLD_SPAN_ID,
23+
TraceMetricKnownFieldKey.METRIC_TYPE,
24+
TraceMetricKnownFieldKey.METRIC_NAME,
25+
TraceMetricKnownFieldKey.TIMESTAMP,
26+
];
27+
1628
/**
1729
* These are fields that should be hidden in metric details view when receiving all data from the API.
1830
*/
@@ -26,6 +38,7 @@ export const HiddenTraceMetricDetailFields: TraceMetricFieldKey[] = [
2638
TraceMetricKnownFieldKey.TRACE_FLAGS,
2739
TraceMetricKnownFieldKey.METRIC_NAME,
2840
TraceMetricKnownFieldKey.METRIC_TYPE,
41+
TraceMetricKnownFieldKey.CLIENT_SAMPLE_RATE,
2942
];
3043

3144
export const HiddenTraceMetricSearchFields: TraceMetricFieldKey[] = [
@@ -38,6 +51,31 @@ export const HiddenTraceMetricGroupByFields: TraceMetricFieldKey[] = [
3851
...HiddenTraceMetricSearchFields,
3952
];
4053

54+
export const TraceSamplesTableStatColumns: VirtualTableSampleColumnKey[] = [
55+
VirtualTableSampleColumnKey.LOGS,
56+
VirtualTableSampleColumnKey.SPANS,
57+
VirtualTableSampleColumnKey.ERRORS,
58+
];
59+
60+
export const TraceSamplesTableColumns: Array<
61+
TraceMetricFieldKey | VirtualTableSampleColumnKey
62+
> = [
63+
VirtualTableSampleColumnKey.EXPAND_ROW,
64+
TraceMetricKnownFieldKey.TIMESTAMP,
65+
TraceMetricKnownFieldKey.TRACE,
66+
...TraceSamplesTableStatColumns,
67+
TraceMetricKnownFieldKey.METRIC_VALUE,
68+
];
69+
70+
export const TraceSamplesTableEmbeddedColumns: Array<
71+
TraceMetricFieldKey | VirtualTableSampleColumnKey
72+
> = [
73+
VirtualTableSampleColumnKey.EXPAND_ROW,
74+
TraceMetricKnownFieldKey.TIMESTAMP,
75+
TraceMetricKnownFieldKey.METRIC_NAME,
76+
TraceMetricKnownFieldKey.METRIC_VALUE,
77+
];
78+
4179
export const OPTIONS_BY_TYPE: Record<string, Array<SelectOption<string>>> = {
4280
counter: [
4381
{

static/app/views/explore/metrics/hooks/useMetricSamplesTable.spec.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ describe('useMetricSamplesTable', () => {
5151
fields: [],
5252
limit: 100,
5353
ingestionDelaySeconds: 0,
54-
enabled: true,
5554
}),
5655
{
5756
additionalWrapper: MockMetricQueryParamsContext,

0 commit comments

Comments
 (0)