diff --git a/pkg/services/navtree/navtreeimpl/navtree.go b/pkg/services/navtree/navtreeimpl/navtree.go
index 826d367c82f..5db53386053 100644
--- a/pkg/services/navtree/navtreeimpl/navtree.go
+++ b/pkg/services/navtree/navtreeimpl/navtree.go
@@ -401,6 +401,16 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Alert groups", SubTitle: "See grouped alerts from an Alertmanager instance", Id: "groups", Url: s.cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
}
+ if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingCentralAlertHistory) {
+ alertChildNavs = append(alertChildNavs, &navtree.NavLink{
+ Text: "Alert history",
+ SubTitle: "History of events that were generated by by your Grafana-managed alert rules. Silences and Mute timmings are ignored.",
+ Id: "alerts-history",
+ Url: s.cfg.AppSubURL + "/alerting/history",
+ Icon: "history",
+ })
+ }
+
if c.SignedInUser.GetOrgRole() == org.RoleAdmin {
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
Text: "Settings", Id: "alerting-admin", Url: s.cfg.AppSubURL + "/alerting/admin",
diff --git a/public/app/features/alerting/routes.tsx b/public/app/features/alerting/routes.tsx
index c34f680bc8e..b1053160979 100644
--- a/public/app/features/alerting/routes.tsx
+++ b/public/app/features/alerting/routes.tsx
@@ -169,6 +169,16 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
() => import(/* webpackChunkName: "AlertGroups" */ 'app/features/alerting/unified/AlertGroups')
),
},
+ {
+ path: '/alerting/history/',
+ roles: evaluateAccess([
+ AccessControlAction.AlertingInstanceRead,
+ AccessControlAction.AlertingInstancesExternalRead,
+ ]),
+ component: importAlertingComponent(
+ () => import(/* webpackChunkName: "CentralAlertHistory" */ 'app/features/alerting/unified/CentralAlertHistory')
+ ),
+ },
{
path: '/alerting/new/:type?',
pageClass: 'page-alerting',
diff --git a/public/app/features/alerting/unified/CentralAlertHistory.tsx b/public/app/features/alerting/unified/CentralAlertHistory.tsx
new file mode 100644
index 00000000000..317cf336366
--- /dev/null
+++ b/public/app/features/alerting/unified/CentralAlertHistory.tsx
@@ -0,0 +1,151 @@
+import React, { useCallback, useMemo, useState } from 'react';
+import { useForm } from 'react-hook-form';
+
+import { NavModelItem, getDefaultTimeRange } from '@grafana/data';
+import { isFetchError } from '@grafana/runtime';
+import { Alert, Button, Field, Icon, Input, Label, Stack, withErrorBoundary } from '@grafana/ui';
+import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound';
+
+import { stateHistoryApi } from './api/stateHistoryApi';
+import { AlertingPageWrapper } from './components/AlertingPageWrapper';
+import { STATE_HISTORY_POLLING_INTERVAL } from './components/rules/state-history/LokiStateHistory';
+import { LogRecord } from './components/rules/state-history/common';
+import { useRuleHistoryRecords } from './components/rules/state-history/useRuleHistoryRecords';
+import { stringifyErrorLike } from './utils/misc';
+
+const CentralAlertHistory = (): JSX.Element => {
+ const { useGetRuleHistoryQuery } = stateHistoryApi;
+ // Filter state
+ const [eventsFilter, setEventsFilter] = useState('');
+ const { getValues, setValue, register, handleSubmit } = useForm({ defaultValues: { query: '' } }); // form for search field
+
+ const onFilterCleared = useCallback(() => {
+ setEventsFilter('');
+ setValue('query', '');
+ }, [setEventsFilter, setValue]);
+
+ // We prefer log count-based limit rather than time-based, but the API doesn't support it yet
+ const queryTimeRange = useMemo(() => getDefaultTimeRange(), []);
+
+ const {
+ currentData: stateHistory,
+ isLoading,
+ isError,
+ error,
+ } = useGetRuleHistoryQuery(
+ {
+ from: queryTimeRange.from.unix(),
+ to: queryTimeRange.to.unix(),
+ limit: 250,
+ },
+ {
+ refetchOnFocus: true,
+ refetchOnReconnect: true,
+ pollingInterval: STATE_HISTORY_POLLING_INTERVAL,
+ }
+ );
+ const { historyRecords, findCommonLabels } = useRuleHistoryRecords(
+ stateHistory
+ // instancesFilter
+ );
+ const defaultPageNav: NavModelItem = {
+ id: 'alerts-history',
+ text: '',
+ };
+
+ if (isError) {
+ return (
+