The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/plugins/panel/alertlist/module.tsx

330 lines
10 KiB

import React from 'react';
import { DataSourceInstanceSettings, PanelPlugin } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Button, Stack, TagsInput } from '@grafana/ui';
import { OldFolderPicker } from 'app/core/components/Select/OldFolderPicker';
import {
ALL_FOLDER,
GENERAL_FOLDER,
ReadonlyFolderPicker,
} from 'app/core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker';
import { PermissionLevelString } from 'app/types';
import { GRAFANA_DATASOURCE_NAME } from '../../../features/alerting/unified/utils/datasource';
import { AlertList } from './AlertList';
import { alertListPanelMigrationHandler } from './AlertListMigrationHandler';
import { GroupBy } from './GroupByWithLoading';
import { UnifiedAlertList } from './UnifiedAlertList';
import { AlertListSuggestionsSupplier } from './suggestions';
import { AlertListOptions, GroupMode, ShowOption, SortOrder, UnifiedAlertListOptions, ViewMode } from './types';
function showIfCurrentState(options: AlertListOptions) {
return options.showOptions === ShowOption.Current;
}
const alertList = new PanelPlugin<AlertListOptions>(AlertList)
.setPanelOptions((builder) => {
builder
.addSelect({
name: 'Show',
path: 'showOptions',
settings: {
options: [
{ label: 'Current state', value: ShowOption.Current },
{ label: 'Recent state changes', value: ShowOption.RecentChanges },
],
},
defaultValue: ShowOption.Current,
category: ['Options'],
})
.addNumberInput({
name: 'Max items',
path: 'maxItems',
defaultValue: 10,
category: ['Options'],
})
.addSelect({
name: 'Sort order',
path: 'sortOrder',
settings: {
options: [
{ label: 'Alphabetical (asc)', value: SortOrder.AlphaAsc },
{ label: 'Alphabetical (desc)', value: SortOrder.AlphaDesc },
{ label: 'Importance', value: SortOrder.Importance },
{ label: 'Time (asc)', value: SortOrder.TimeAsc },
{ label: 'Time (desc)', value: SortOrder.TimeDesc },
],
},
defaultValue: SortOrder.AlphaAsc,
category: ['Options'],
})
.addBooleanSwitch({
path: 'dashboardAlerts',
name: 'Alerts from this dashboard',
defaultValue: false,
category: ['Options'],
})
.addTextInput({
path: 'alertName',
name: 'Alert name',
defaultValue: '',
category: ['Filter'],
showIf: showIfCurrentState,
})
.addTextInput({
path: 'dashboardTitle',
name: 'Dashboard title',
defaultValue: '',
category: ['Filter'],
showIf: showIfCurrentState,
})
.addCustomEditor({
path: 'folderId',
name: 'Folder',
id: 'folderId',
defaultValue: null,
editor: function RenderFolderPicker({ value, onChange }) {
return (
<ReadonlyFolderPicker
initialFolderId={value}
onChange={(folder) => onChange(folder?.id)}
extraFolders={[ALL_FOLDER, GENERAL_FOLDER]}
/>
);
},
category: ['Filter'],
showIf: showIfCurrentState,
})
.addCustomEditor({
id: 'tags',
path: 'tags',
name: 'Tags',
description: '',
defaultValue: [],
editor(props) {
return <TagsInput tags={props.value} onChange={props.onChange} />;
},
category: ['Filter'],
showIf: showIfCurrentState,
})
.addBooleanSwitch({
path: 'stateFilter.ok',
name: 'Ok',
defaultValue: false,
category: ['State filter'],
showIf: showIfCurrentState,
})
.addBooleanSwitch({
path: 'stateFilter.paused',
name: 'Paused',
defaultValue: false,
category: ['State filter'],
showIf: showIfCurrentState,
})
.addBooleanSwitch({
path: 'stateFilter.no_data',
name: 'No data',
defaultValue: false,
category: ['State filter'],
showIf: showIfCurrentState,
})
.addBooleanSwitch({
path: 'stateFilter.execution_error',
name: 'Execution error',
defaultValue: false,
category: ['State filter'],
showIf: showIfCurrentState,
})
.addBooleanSwitch({
path: 'stateFilter.alerting',
name: 'Alerting',
defaultValue: false,
category: ['State filter'],
showIf: showIfCurrentState,
})
.addBooleanSwitch({
path: 'stateFilter.pending',
name: 'Pending',
defaultValue: false,
category: ['State filter'],
showIf: showIfCurrentState,
});
})
VisualizationSelection: Real previews of suitable visualisation and options based on current data (#40527) * Initial pass to move panel state to it's own, and make it by key not panel.id * Progress * Not making much progress, having panel.key be mutable is causing a lot of issues * Think this is starting to work * Began fixing tests * Add selector * Bug fixes and changes to cleanup, and fixing all flicking when switching library panels * Removed console.log * fixes after merge * fixing tests * fixing tests * Added new test for changePlugin thunk * Initial struture in place * responding to state changes in another part of the state * bha * going in a different direction * This is getting exciting * minor * More structure * More real * Added builder to reduce boiler plate * Lots of progress * Adding more visualizations * More smarts * tweaks * suggestions * Move to separate view * Refactoring to builder concept * Before hover preview test * Increase line width in preview * More suggestions * Removed old elements of onSuggestVisualizations * Don't call suggestion suppliers if there is no data * Restore card styles to only borders * Changing supplier interface to support data vs option suggestion scenario * Renamed functions * Add dynamic width support * not sure about this * Improve suggestions * Improve suggestions * Single grid/list * Store vis select pane & size * Prep for option suggestions * more suggestions * Name/title option for preview cards * Improve barchart suggestions * Support suggestions when there are no data * Minor change * reverted some changes * Improve suggestions for stacking * Removed size option * starting on unit tests, hit cyclic dependency issue * muuu * First test for getting suggestion seems to work, going to bed * add missing file * A basis for more unit tests * More tests * More unit tests * Fixed unit tests * Update * Some extreme scenarios * Added basic e2e test * Added another unit test for changePanelPlugin action * More cleanup * Minor tweak * add wait to e2e test * Renamed function and cleanup of unused function * Adding search support and adding search test to e2e test
4 years ago
.setMigrationHandler(alertListPanelMigrationHandler)
.setSuggestionsSupplier(new AlertListSuggestionsSupplier());
const unifiedAlertList = new PanelPlugin<UnifiedAlertListOptions>(UnifiedAlertList).setPanelOptions((builder) => {
builder
.addRadio({
path: 'viewMode',
name: 'View mode',
description: 'Toggle between list view and stat view',
defaultValue: ViewMode.List,
settings: {
options: [
{ label: 'List', value: ViewMode.List },
{ label: 'Stat', value: ViewMode.Stat },
],
},
category: ['Options'],
})
.addRadio({
path: 'groupMode',
name: 'Group mode',
description: 'How alert instances should be grouped',
defaultValue: GroupMode.Default,
settings: {
options: [
{ value: GroupMode.Default, label: 'Default grouping' },
{ value: GroupMode.Custom, label: 'Custom grouping' },
],
},
category: ['Options'],
})
.addCustomEditor({
path: 'groupBy',
name: 'Group by',
description: 'Filter alerts using label querying',
id: 'groupBy',
defaultValue: [],
showIf: (options) => options.groupMode === GroupMode.Custom,
category: ['Options'],
editor: (props) => {
return (
<GroupBy
id={props.id ?? 'groupBy'}
defaultValue={props.value.map((value: string) => ({ label: value, value }))}
onChange={props.onChange}
dataSource={props.context.options.datasource}
/>
);
},
})
.addNumberInput({
name: 'Max items',
path: 'maxItems',
description: 'Maximum alerts to display',
defaultValue: 20,
category: ['Options'],
})
.addSelect({
name: 'Sort order',
path: 'sortOrder',
description: 'Sort order of alerts and alert instances',
settings: {
options: [
{ label: 'Alphabetical (asc)', value: SortOrder.AlphaAsc },
{ label: 'Alphabetical (desc)', value: SortOrder.AlphaDesc },
{ label: 'Importance', value: SortOrder.Importance },
{ label: 'Time (asc)', value: SortOrder.TimeAsc },
{ label: 'Time (desc)', value: SortOrder.TimeDesc },
],
},
defaultValue: SortOrder.AlphaAsc,
category: ['Options'],
})
.addBooleanSwitch({
path: 'dashboardAlerts',
name: 'Alerts linked to this dashboard',
description: 'Only show alerts linked to this dashboard',
defaultValue: false,
category: ['Options'],
})
.addTextInput({
path: 'alertName',
name: 'Alert name',
description: 'Filter for alerts containing this text',
defaultValue: '',
category: ['Filter'],
})
.addTextInput({
path: 'alertInstanceLabelFilter',
name: 'Alert instance label',
description: 'Filter alert instances using label querying, ex: {severity="critical", instance=~"cluster-us-.+"}',
defaultValue: '',
category: ['Filter'],
})
.addCustomEditor({
path: 'datasource',
name: 'Datasource',
description: 'Filter from alert source',
id: 'datasource',
defaultValue: null,
editor: function RenderDatasourcePicker(props) {
return (
<Stack gap={1}>
<DataSourcePicker
{...props}
type={['prometheus', 'loki', 'grafana']}
noDefault
current={props.value}
onChange={(ds: DataSourceInstanceSettings) => props.onChange(ds.name)}
/>
<Button variant="secondary" onClick={() => props.onChange(null)}>
Clear
</Button>
</Stack>
);
},
category: ['Filter'],
})
.addCustomEditor({
showIf: (options) => options.datasource === GRAFANA_DATASOURCE_NAME || !Boolean(options.datasource),
path: 'folder',
name: 'Folder',
description: 'Filter for alerts in the selected folder (only for Grafana alerts)',
id: 'folder',
defaultValue: null,
editor: function RenderFolderPicker(props) {
return (
<OldFolderPicker
enableReset={true}
showRoot={false}
allowEmpty={true}
initialTitle={props.value?.title}
initialFolderUid={props.value?.uid}
permissionLevel={PermissionLevelString.View}
onClear={() => props.onChange('')}
{...props}
/>
);
},
category: ['Filter'],
})
.addBooleanSwitch({
path: 'stateFilter.firing',
name: 'Alerting / Firing',
defaultValue: true,
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'stateFilter.pending',
name: 'Pending',
defaultValue: true,
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'stateFilter.noData',
name: 'No Data',
defaultValue: false,
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'stateFilter.normal',
name: 'Normal',
defaultValue: false,
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'stateFilter.error',
name: 'Error',
defaultValue: true,
category: ['Alert state filter'],
});
});
export const plugin = config.unifiedAlertingEnabled ? unifiedAlertList : alertList;