mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
Unified Alerting: UI explains "match all" case in Notification Policies. (#47574)
This change makes it explicit in the UI that a notification policy with no matching labels matches all alerts that it processes. There are visual changes in both the Notification Policy editor, and in the Notification Policy table where matching columns are shown. It's valid to have a notification policy with no label matchers attached. Such a policy matches all alerts sent to it. It's a common stumbling block for users. Where are all my alerts going? Co-authored-by: gillesdemey <gilles.de.mey@gmail.com>
This commit is contained in:
parent
7a8437020d
commit
51c98b182d
@ -14,6 +14,8 @@ import {
|
||||
Select,
|
||||
Switch,
|
||||
useStyles2,
|
||||
Badge,
|
||||
VerticalGroup,
|
||||
} from '@grafana/ui';
|
||||
import { AmRouteReceiver, FormAmRoute } from '../../types/amroutes';
|
||||
import {
|
||||
@ -56,73 +58,85 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
|
||||
<FieldArray name="object_matchers" control={control}>
|
||||
{({ fields, append, remove }) => (
|
||||
<>
|
||||
<div>Matching labels</div>
|
||||
<div className={styles.matchersContainer}>
|
||||
{fields.map((field, index) => {
|
||||
const localPath = `object_matchers[${index}]`;
|
||||
return (
|
||||
<HorizontalGroup key={field.id} align="flex-start">
|
||||
<Field
|
||||
label="Label"
|
||||
invalid={!!errors.object_matchers?.[index]?.name}
|
||||
error={errors.object_matchers?.[index]?.name?.message}
|
||||
>
|
||||
<Input
|
||||
{...register(`${localPath}.name`, { required: 'Field is required' })}
|
||||
defaultValue={field.name}
|
||||
placeholder="label"
|
||||
/>
|
||||
</Field>
|
||||
<Field label={'Operator'}>
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
{...field}
|
||||
className={styles.matchersOperator}
|
||||
onChange={(value) => onChange(value?.value)}
|
||||
options={matcherFieldOptions}
|
||||
aria-label="Operator"
|
||||
menuShouldPortal
|
||||
<VerticalGroup justify="flex-start" spacing="md">
|
||||
<div>Matching labels</div>
|
||||
{fields.length === 0 && (
|
||||
<Badge
|
||||
color="orange"
|
||||
className={styles.noMatchersWarning}
|
||||
icon="exclamation-triangle"
|
||||
text="If no matchers are specified, this notification policy will handle all alert instances."
|
||||
/>
|
||||
)}
|
||||
{fields.length > 0 && (
|
||||
<div className={styles.matchersContainer}>
|
||||
{fields.map((field, index) => {
|
||||
const localPath = `object_matchers[${index}]`;
|
||||
return (
|
||||
<HorizontalGroup key={field.id} align="flex-start">
|
||||
<Field
|
||||
label="Label"
|
||||
invalid={!!errors.object_matchers?.[index]?.name}
|
||||
error={errors.object_matchers?.[index]?.name?.message}
|
||||
>
|
||||
<Input
|
||||
{...register(`${localPath}.name`, { required: 'Field is required' })}
|
||||
defaultValue={field.name}
|
||||
placeholder="label"
|
||||
/>
|
||||
)}
|
||||
defaultValue={field.operator}
|
||||
control={control}
|
||||
name={`${localPath}.operator` as const}
|
||||
rules={{ required: { value: true, message: 'Required.' } }}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Value"
|
||||
invalid={!!errors.object_matchers?.[index]?.value}
|
||||
error={errors.object_matchers?.[index]?.value?.message}
|
||||
>
|
||||
<Input
|
||||
{...register(`${localPath}.value`, { required: 'Field is required' })}
|
||||
defaultValue={field.value}
|
||||
placeholder="value"
|
||||
/>
|
||||
</Field>
|
||||
<IconButton
|
||||
className={styles.removeButton}
|
||||
tooltip="Remove matcher"
|
||||
name={'trash-alt'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
Remove
|
||||
</IconButton>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
className={styles.addMatcherBtn}
|
||||
icon="plus"
|
||||
onClick={() => append(emptyArrayFieldMatcher)}
|
||||
variant="secondary"
|
||||
type="button"
|
||||
>
|
||||
Add matcher
|
||||
</Button>
|
||||
</Field>
|
||||
<Field label={'Operator'}>
|
||||
<InputControl
|
||||
render={({ field: { onChange, ref, ...field } }) => (
|
||||
<Select
|
||||
{...field}
|
||||
className={styles.matchersOperator}
|
||||
onChange={(value) => onChange(value?.value)}
|
||||
options={matcherFieldOptions}
|
||||
aria-label="Operator"
|
||||
menuShouldPortal
|
||||
/>
|
||||
)}
|
||||
defaultValue={field.operator}
|
||||
control={control}
|
||||
name={`${localPath}.operator` as const}
|
||||
rules={{ required: { value: true, message: 'Required.' } }}
|
||||
/>
|
||||
</Field>
|
||||
<Field
|
||||
label="Value"
|
||||
invalid={!!errors.object_matchers?.[index]?.value}
|
||||
error={errors.object_matchers?.[index]?.value?.message}
|
||||
>
|
||||
<Input
|
||||
{...register(`${localPath}.value`, { required: 'Field is required' })}
|
||||
defaultValue={field.value}
|
||||
placeholder="value"
|
||||
/>
|
||||
</Field>
|
||||
<IconButton
|
||||
className={styles.removeButton}
|
||||
tooltip="Remove matcher"
|
||||
name={'trash-alt'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
Remove
|
||||
</IconButton>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
className={styles.addMatcherBtn}
|
||||
icon="plus"
|
||||
onClick={() => append(emptyArrayFieldMatcher)}
|
||||
variant="secondary"
|
||||
type="button"
|
||||
>
|
||||
Add matcher
|
||||
</Button>
|
||||
</VerticalGroup>
|
||||
</>
|
||||
)}
|
||||
</FieldArray>
|
||||
@ -376,5 +390,8 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
margin-left: ${theme.spacing(1.5)};
|
||||
}
|
||||
`,
|
||||
noMatchersWarning: css`
|
||||
padding: ${theme.spacing(1)} ${theme.spacing(2)};
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
@ -90,7 +90,13 @@ export const AmRoutesTable: FC<AmRoutesTableProps> = ({
|
||||
id: 'matchingCriteria',
|
||||
label: 'Matching labels',
|
||||
// eslint-disable-next-line react/display-name
|
||||
renderCell: (item) => <Matchers matchers={item.data.object_matchers.map(matcherFieldToMatcher)} />,
|
||||
renderCell: (item) => {
|
||||
return item.data.object_matchers.length ? (
|
||||
<Matchers matchers={item.data.object_matchers.map(matcherFieldToMatcher)} />
|
||||
) : (
|
||||
<span>Matches all alert instances</span>
|
||||
);
|
||||
},
|
||||
size: 10,
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user