Skip to content

Commit 4874377

Browse files
authored
feat(log-alerts): Add all supported aggregation types (#95364)
Adds support for all aggregation types that explore supports for logs.
1 parent 19ed5f5 commit 4874377

File tree

2 files changed

+114
-26
lines changed

2 files changed

+114
-26
lines changed

static/app/views/alerts/rules/metric/eapField.spec.tsx

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,73 @@ describe('EAPField', () => {
242242
// this corresponds to the `spans` input
243243
expect(inputs[1]).toBeDisabled();
244244
});
245+
it('renders count_unique with string arguments for logs', async () => {
246+
function Component() {
247+
const [aggregate, setAggregate] = useState('count(message)');
248+
return (
249+
<TraceItemAttributeProvider traceItemType={TraceItemDataset.LOGS} enabled>
250+
<EAPField
251+
aggregate={aggregate}
252+
onChange={setAggregate}
253+
eventTypes={[EventTypes.TRACE_ITEM_LOG]}
254+
/>
255+
</TraceItemAttributeProvider>
256+
);
257+
}
258+
259+
render(<Component />);
260+
261+
expect(fieldsMock).toHaveBeenCalledWith(
262+
`/organizations/${organization.slug}/trace-items/attributes/`,
263+
expect.objectContaining({
264+
query: expect.objectContaining({attributeType: 'number', itemType: 'logs'}),
265+
})
266+
);
267+
expect(fieldsMock).toHaveBeenCalledWith(
268+
`/organizations/${organization.slug}/trace-items/attributes/`,
269+
expect.objectContaining({
270+
query: expect.objectContaining({attributeType: 'string', itemType: 'logs'}),
271+
})
272+
);
273+
await userEvent.click(screen.getByText('count'));
274+
await userEvent.click(await screen.findByText('count_unique'));
275+
276+
expect(screen.getByText('count_unique')).toBeInTheDocument();
277+
await userEvent.click(screen.getByText('message'));
278+
expect(screen.getByText('severity')).toBeInTheDocument();
279+
});
280+
it('renders numeric aggregates with numeric arguments for logs', async () => {
281+
function Component() {
282+
const [aggregate, setAggregate] = useState('count(message)');
283+
return (
284+
<TraceItemAttributeProvider traceItemType={TraceItemDataset.LOGS} enabled>
285+
<EAPField
286+
aggregate={aggregate}
287+
onChange={setAggregate}
288+
eventTypes={[EventTypes.TRACE_ITEM_LOG]}
289+
/>
290+
</TraceItemAttributeProvider>
291+
);
292+
}
293+
294+
render(<Component />);
295+
296+
expect(fieldsMock).toHaveBeenCalledWith(
297+
`/organizations/${organization.slug}/trace-items/attributes/`,
298+
expect.objectContaining({
299+
query: expect.objectContaining({attributeType: 'number', itemType: 'logs'}),
300+
})
301+
);
302+
expect(fieldsMock).toHaveBeenCalledWith(
303+
`/organizations/${organization.slug}/trace-items/attributes/`,
304+
expect.objectContaining({
305+
query: expect.objectContaining({attributeType: 'string', itemType: 'logs'}),
306+
})
307+
);
308+
await userEvent.click(screen.getByText('count'));
309+
await userEvent.click(await screen.findByText('sum'));
310+
311+
expect(screen.getByText('sum')).toBeInTheDocument();
312+
expect(screen.getByText('severity_number')).toBeInTheDocument();
313+
});
245314
});

static/app/views/alerts/rules/metric/eapField.tsx

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@ import styled from '@emotion/styled';
44
import {Select} from 'sentry/components/core/select';
55
import {t} from 'sentry/locale';
66
import {space} from 'sentry/styles/space';
7-
import type {TagCollection} from 'sentry/types/group';
87
import {defined} from 'sentry/utils';
98
import {parseFunction} from 'sentry/utils/discover/fields';
109
import {
1110
AggregationKey,
1211
ALLOWED_EXPLORE_VISUALIZE_AGGREGATES,
1312
NO_ARGUMENT_SPAN_AGGREGATES,
14-
prettifyTagKey,
1513
} from 'sentry/utils/fields';
1614
import {Dataset, type EventTypes} from 'sentry/views/alerts/rules/metric/types';
1715
import {getTraceItemTypeForDatasetAndEventType} from 'sentry/views/alerts/wizard/utils';
@@ -20,7 +18,10 @@ import {
2018
updateVisualizeAggregate,
2119
} from 'sentry/views/explore/contexts/pageParamsContext/visualizes';
2220
import {useTraceItemAttributes} from 'sentry/views/explore/contexts/traceItemAttributeContext';
23-
import type {OurLogsAggregate} from 'sentry/views/explore/logs/types';
21+
import {
22+
OurLogKnownFieldKey,
23+
type OurLogsAggregate,
24+
} from 'sentry/views/explore/logs/types';
2425
import {TraceItemDataset} from 'sentry/views/explore/types';
2526

2627
const DEFAULT_EAP_AGGREGATION = 'count';
@@ -42,11 +43,21 @@ const SPAN_OPERATIONS = [
4243
];
4344

4445
const LOG_OPERATIONS = [
45-
{
46-
label: AggregationKey.COUNT,
47-
value: AggregationKey.COUNT,
48-
},
49-
] satisfies Array<{label: string; value: OurLogsAggregate}>;
46+
AggregationKey.COUNT,
47+
AggregationKey.COUNT_UNIQUE,
48+
AggregationKey.SUM,
49+
AggregationKey.AVG,
50+
AggregationKey.P50,
51+
AggregationKey.P75,
52+
AggregationKey.P90,
53+
AggregationKey.P95,
54+
AggregationKey.P99,
55+
AggregationKey.MIN,
56+
AggregationKey.MAX,
57+
].map(aggregate => ({
58+
label: aggregate,
59+
value: aggregate as OurLogsAggregate,
60+
})) satisfies Array<{label: string; value: OurLogsAggregate}>;
5061

5162
function EAPFieldWrapper({aggregate, onChange, eventTypes}: Props) {
5263
return <EAPField aggregate={aggregate} onChange={onChange} eventTypes={eventTypes} />;
@@ -69,15 +80,8 @@ function EAPField({aggregate, onChange, eventTypes}: Props) {
6980

7081
const storedTags =
7182
aggregation === AggregationKey.COUNT_UNIQUE ? storedStringTags : storedNumberTags;
72-
const numberTags: TagCollection = useMemo(() => {
73-
const availableTags: TagCollection = storedTags;
74-
if (field && !defined(storedTags[field])) {
75-
availableTags[field] = {key: field, name: prettifyTagKey(field)};
76-
}
77-
return availableTags;
78-
}, [field, storedTags]);
7983

80-
const fieldsArray = Object.values(numberTags);
84+
const fieldsArray = Object.values(storedTags);
8185

8286
// When using the async variant of SelectControl, we need to pass in an option object instead of just the value
8387
const [lockOptions, selectedOption] = useMemo(() => {
@@ -105,7 +109,7 @@ function EAPField({aggregate, onChange, eventTypes}: Props) {
105109
return;
106110
}
107111

108-
const selectedMeta = field ? numberTags[field] : undefined;
112+
const selectedMeta = field ? storedTags[field] : undefined;
109113
if (!field || !selectedMeta) {
110114
const newSelection = fieldsArray[0];
111115
if (newSelection) {
@@ -114,29 +118,44 @@ function EAPField({aggregate, onChange, eventTypes}: Props) {
114118
onChange(DEFAULT_EAP_METRICS_ALERT_FIELD, {});
115119
}
116120
}
117-
}, [lockOptions, onChange, aggregate, aggregation, field, numberTags, fieldsArray]);
121+
}, [lockOptions, onChange, aggregate, aggregation, field, storedTags, fieldsArray]);
118122

119123
const handleFieldChange = useCallback(
120124
(option: any) => {
121-
const selectedMeta = numberTags[option.value];
125+
const selectedMeta = storedTags[option.value];
122126
if (!selectedMeta) {
123127
return;
124128
}
125129
onChange(`${aggregation}(${selectedMeta.key})`, {});
126130
},
127-
[numberTags, onChange, aggregation]
131+
[storedTags, onChange, aggregation]
128132
);
129133

130134
const handleOperationChange = useCallback(
131135
(option: any) => {
132-
const newAggregate = updateVisualizeAggregate({
133-
newAggregate: option.value,
134-
oldAggregate: aggregation || DEFAULT_EAP_AGGREGATION,
135-
oldArgument: field || DEFAULT_EAP_FIELD,
136-
});
136+
let newAggregate: string;
137+
if (traceItemType === TraceItemDataset.LOGS) {
138+
if ([AggregationKey.COUNT, AggregationKey.COUNT_UNIQUE].includes(option.value)) {
139+
newAggregate = `${option.value}(${OurLogKnownFieldKey.MESSAGE})`;
140+
} else if (NO_ARGUMENT_SPAN_AGGREGATES.includes(option.value as AggregationKey)) {
141+
newAggregate = `${option.value}()`;
142+
} else {
143+
const argument =
144+
field && defined(storedNumberTags[field])
145+
? field
146+
: (Object.values(storedNumberTags)?.[0]?.key ?? '');
147+
newAggregate = `${option.value}(${argument})`;
148+
}
149+
} else {
150+
newAggregate = updateVisualizeAggregate({
151+
newAggregate: option.value,
152+
oldAggregate: aggregation || DEFAULT_EAP_AGGREGATION,
153+
oldArgument: field || DEFAULT_EAP_FIELD,
154+
});
155+
}
137156
onChange(newAggregate, {});
138157
},
139-
[aggregation, field, onChange]
158+
[aggregation, field, onChange, storedNumberTags, traceItemType]
140159
);
141160

142161
// As SelectControl does not support an options size limit out of the box

0 commit comments

Comments
 (0)