Alerting: Add Triage feature toggle (#110326)
* Add state history config to frontend config object * Add alertingTriage feature toggle * Add Triage menu entry * Add old state history config props for backward compatibility
This commit is contained in:
@@ -100,15 +100,24 @@ export interface GrafanaJavascriptAgentConfig {
|
|||||||
apiKey: string;
|
apiKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UnifiedAlertingStateHistoryConfig {
|
||||||
|
backend?: string;
|
||||||
|
primary?: string;
|
||||||
|
prometheusTargetDatasourceUID?: string;
|
||||||
|
prometheusMetricName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface UnifiedAlertingConfig {
|
export interface UnifiedAlertingConfig {
|
||||||
minInterval: string;
|
minInterval: string;
|
||||||
// will be undefined if alerStateHistory is not enabled
|
stateHistory?: UnifiedAlertingStateHistoryConfig;
|
||||||
alertStateHistoryBackend?: string;
|
|
||||||
// will be undefined if implementation is not "multiple"
|
|
||||||
alertStateHistoryPrimary?: string;
|
|
||||||
recordingRulesEnabled?: boolean;
|
recordingRulesEnabled?: boolean;
|
||||||
// will be undefined if no default datasource is configured
|
|
||||||
defaultRecordingRulesTargetDatasourceUID?: string;
|
defaultRecordingRulesTargetDatasourceUID?: string;
|
||||||
|
|
||||||
|
// Backward compatibility aliases - deprecated
|
||||||
|
/** @deprecated Use stateHistory.backend instead */
|
||||||
|
alertStateHistoryBackend?: string;
|
||||||
|
/** @deprecated Use stateHistory.primary instead */
|
||||||
|
alertStateHistoryPrimary?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Supported OAuth services
|
/** Supported OAuth services
|
||||||
|
|||||||
@@ -1106,4 +1106,9 @@ export interface FeatureToggles {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
teamFolders?: boolean;
|
teamFolders?: boolean;
|
||||||
|
/**
|
||||||
|
* Enables the alerting triage feature
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
alertingTriage?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,10 +186,18 @@ export class GrafanaBootConfig {
|
|||||||
unifiedAlertingEnabled = false;
|
unifiedAlertingEnabled = false;
|
||||||
unifiedAlerting: UnifiedAlertingConfig = {
|
unifiedAlerting: UnifiedAlertingConfig = {
|
||||||
minInterval: '',
|
minInterval: '',
|
||||||
alertStateHistoryBackend: undefined,
|
stateHistory: {
|
||||||
alertStateHistoryPrimary: undefined,
|
backend: undefined,
|
||||||
|
primary: undefined,
|
||||||
|
prometheusTargetDatasourceUID: undefined,
|
||||||
|
prometheusMetricName: undefined,
|
||||||
|
},
|
||||||
recordingRulesEnabled: false,
|
recordingRulesEnabled: false,
|
||||||
defaultRecordingRulesTargetDatasourceUID: undefined,
|
defaultRecordingRulesTargetDatasourceUID: undefined,
|
||||||
|
|
||||||
|
// Backward compatibility fields - populated by backend
|
||||||
|
alertStateHistoryBackend: undefined,
|
||||||
|
alertStateHistoryPrimary: undefined,
|
||||||
};
|
};
|
||||||
applicationInsightsConnectionString?: string;
|
applicationInsightsConnectionString?: string;
|
||||||
applicationInsightsEndpointUrl?: string;
|
applicationInsightsEndpointUrl?: string;
|
||||||
|
|||||||
@@ -93,12 +93,22 @@ type FrontendSettingsAnalyticsDTO struct {
|
|||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FrontendSettingsUnifiedAlertingStateHistoryDTO struct {
|
||||||
|
Backend string `json:"backend,omitempty"`
|
||||||
|
Primary string `json:"primary,omitempty"`
|
||||||
|
PrometheusTargetDatasourceUID string `json:"prometheusTargetDatasourceUID,omitempty"`
|
||||||
|
PrometheusMetricName string `json:"prometheusMetricName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type FrontendSettingsUnifiedAlertingDTO struct {
|
type FrontendSettingsUnifiedAlertingDTO struct {
|
||||||
MinInterval string `json:"minInterval"`
|
MinInterval string `json:"minInterval"`
|
||||||
AlertStateHistoryBackend string `json:"alertStateHistoryBackend,omitempty"`
|
StateHistory *FrontendSettingsUnifiedAlertingStateHistoryDTO `json:"stateHistory,omitempty"`
|
||||||
AlertStateHistoryPrimary string `json:"alertStateHistoryPrimary,omitempty"`
|
RecordingRulesEnabled bool `json:"recordingRulesEnabled"`
|
||||||
RecordingRulesEnabled bool `json:"recordingRulesEnabled"`
|
DefaultRecordingRulesTargetDatasourceUID string `json:"defaultRecordingRulesTargetDatasourceUID,omitempty"`
|
||||||
DefaultRecordingRulesTargetDatasourceUID string `json:"defaultRecordingRulesTargetDatasourceUID,omitempty"`
|
|
||||||
|
// Backward compatibility fields - deprecated
|
||||||
|
AlertStateHistoryBackend string `json:"alertStateHistoryBackend,omitempty"`
|
||||||
|
AlertStateHistoryPrimary string `json:"alertStateHistoryPrimary,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enterprise-only
|
// Enterprise-only
|
||||||
|
|||||||
@@ -348,6 +348,18 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hs.Cfg.UnifiedAlerting.StateHistory.Enabled {
|
if hs.Cfg.UnifiedAlerting.StateHistory.Enabled {
|
||||||
|
frontendSettings.UnifiedAlerting.StateHistory = &dtos.FrontendSettingsUnifiedAlertingStateHistoryDTO{
|
||||||
|
Backend: hs.Cfg.UnifiedAlerting.StateHistory.Backend,
|
||||||
|
Primary: hs.Cfg.UnifiedAlerting.StateHistory.MultiPrimary,
|
||||||
|
}
|
||||||
|
if hs.Cfg.UnifiedAlerting.StateHistory.PrometheusTargetDatasourceUID != "" {
|
||||||
|
frontendSettings.UnifiedAlerting.StateHistory.PrometheusTargetDatasourceUID = hs.Cfg.UnifiedAlerting.StateHistory.PrometheusTargetDatasourceUID
|
||||||
|
}
|
||||||
|
if hs.Cfg.UnifiedAlerting.StateHistory.PrometheusMetricName != "" {
|
||||||
|
frontendSettings.UnifiedAlerting.StateHistory.PrometheusMetricName = hs.Cfg.UnifiedAlerting.StateHistory.PrometheusMetricName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate deprecated fields for backward compatibility
|
||||||
frontendSettings.UnifiedAlerting.AlertStateHistoryBackend = hs.Cfg.UnifiedAlerting.StateHistory.Backend
|
frontendSettings.UnifiedAlerting.AlertStateHistoryBackend = hs.Cfg.UnifiedAlerting.StateHistory.Backend
|
||||||
frontendSettings.UnifiedAlerting.AlertStateHistoryPrimary = hs.Cfg.UnifiedAlerting.StateHistory.MultiPrimary
|
frontendSettings.UnifiedAlerting.AlertStateHistoryPrimary = hs.Cfg.UnifiedAlerting.StateHistory.MultiPrimary
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1918,6 +1918,16 @@ var (
|
|||||||
Owner: grafanaFrontendSearchNavOrganise,
|
Owner: grafanaFrontendSearchNavOrganise,
|
||||||
Expression: "false",
|
Expression: "false",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "alertingTriage",
|
||||||
|
Description: "Enables the alerting triage feature",
|
||||||
|
Stage: FeatureStageExperimental,
|
||||||
|
FrontendOnly: true,
|
||||||
|
Owner: grafanaAlertingSquad,
|
||||||
|
HideFromDocs: true,
|
||||||
|
HideFromAdminPage: true,
|
||||||
|
Expression: "false",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -247,3 +247,4 @@ newLogContext,experimental,@grafana/observability-logs,false,false,true
|
|||||||
newClickhouseConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
|
newClickhouseConfigPageDesign,privatePreview,@grafana/partner-datasources,false,false,false
|
||||||
unifiedStorageSearchAfterWriteExperimentalAPI,experimental,@grafana/search-and-storage,false,true,false
|
unifiedStorageSearchAfterWriteExperimentalAPI,experimental,@grafana/search-and-storage,false,true,false
|
||||||
teamFolders,experimental,@grafana/grafana-search-navigate-organise,false,false,false
|
teamFolders,experimental,@grafana/grafana-search-navigate-organise,false,false,false
|
||||||
|
alertingTriage,experimental,@grafana/alerting-squad,false,false,true
|
||||||
|
|||||||
|
@@ -998,4 +998,8 @@ const (
|
|||||||
// FlagTeamFolders
|
// FlagTeamFolders
|
||||||
// Enables team folders functionality
|
// Enables team folders functionality
|
||||||
FlagTeamFolders = "teamFolders"
|
FlagTeamFolders = "teamFolders"
|
||||||
|
|
||||||
|
// FlagAlertingTriage
|
||||||
|
// Enables the alerting triage feature
|
||||||
|
FlagAlertingTriage = "alertingTriage"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -515,6 +515,22 @@
|
|||||||
"codeowner": "@grafana/alerting-squad"
|
"codeowner": "@grafana/alerting-squad"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "alertingTriage",
|
||||||
|
"resourceVersion": "1756386724059",
|
||||||
|
"creationTimestamp": "2025-08-28T13:12:04Z"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"description": "Enables the alerting triage feature",
|
||||||
|
"stage": "experimental",
|
||||||
|
"codeowner": "@grafana/alerting-squad",
|
||||||
|
"frontend": true,
|
||||||
|
"hideFromAdminPage": true,
|
||||||
|
"hideFromDocs": true,
|
||||||
|
"expression": "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "alertingUIOptimizeReducer",
|
"name": "alertingUIOptimizeReducer",
|
||||||
|
|||||||
@@ -428,6 +428,14 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
|
|||||||
hasAccess := ac.HasAccess(s.accessControl, c)
|
hasAccess := ac.HasAccess(s.accessControl, c)
|
||||||
var alertChildNavs []*navtree.NavLink
|
var alertChildNavs []*navtree.NavLink
|
||||||
|
|
||||||
|
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingTriage) {
|
||||||
|
if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) {
|
||||||
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||||
|
Text: "Triage", SubTitle: "Triage alerts", Id: "alert-triage", Url: s.cfg.AppSubURL + "/alerting/triage", Icon: "medkit",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) {
|
if hasAccess(ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) {
|
||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||||
Text: "Alert rules", SubTitle: "Rules that determine whether an alert will fire", Id: "alert-list", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
Text: "Alert rules", SubTitle: "Rules that determine whether an alert will fire", Id: "alert-list", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Navigate } from 'react-router-dom-v5-compat';
|
|||||||
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
|
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
import { GrafanaRouteComponent, RouteDescriptor } from 'app/core/navigation/types';
|
import { GrafanaRouteComponent, RouteDescriptor } from 'app/core/navigation/types';
|
||||||
|
import { AlertingPageWrapper } from 'app/features/alerting/unified/components/AlertingPageWrapper';
|
||||||
import { AccessControlAction } from 'app/types/accessControl';
|
import { AccessControlAction } from 'app/types/accessControl';
|
||||||
|
|
||||||
import { PERMISSIONS_CONTACT_POINTS } from './unified/components/contact-points/permissions';
|
import { PERMISSIONS_CONTACT_POINTS } from './unified/components/contact-points/permissions';
|
||||||
@@ -333,6 +334,14 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (cfg.featureToggles.alertingTriage) {
|
||||||
|
routes.push({
|
||||||
|
path: '/alerting/triage',
|
||||||
|
roles: evaluateAccess([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleExternalRead]),
|
||||||
|
component: () => <AlertingPageWrapper />,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return routes;
|
return routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ interface HistoryProps {
|
|||||||
|
|
||||||
const History = ({ rule }: HistoryProps) => {
|
const History = ({ rule }: HistoryProps) => {
|
||||||
// can be "loki", "multiple" or "annotations"
|
// can be "loki", "multiple" or "annotations"
|
||||||
const stateHistoryBackend = config.unifiedAlerting.alertStateHistoryBackend;
|
const stateHistoryBackend = config.unifiedAlerting.stateHistory?.backend;
|
||||||
// can be "loki" or "annotations"
|
// can be "loki" or "annotations"
|
||||||
const stateHistoryPrimary = config.unifiedAlerting.alertStateHistoryPrimary;
|
const stateHistoryPrimary = config.unifiedAlerting.stateHistory?.primary;
|
||||||
|
|
||||||
// if "loki" is either the backend or the primary, show the new state history implementation
|
// if "loki" is either the backend or the primary, show the new state history implementation
|
||||||
const usingNewAlertStateHistory = [stateHistoryBackend, stateHistoryPrimary].some(
|
const usingNewAlertStateHistory = [stateHistoryBackend, stateHistoryPrimary].some(
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ function useStateHistoryModal() {
|
|||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
// can be "loki", "multiple" or "annotations"
|
// can be "loki", "multiple" or "annotations"
|
||||||
const stateHistoryBackend = config.unifiedAlerting.alertStateHistoryBackend;
|
const stateHistoryBackend = config.unifiedAlerting.stateHistory?.backend;
|
||||||
// can be "loki" or "annotations"
|
// can be "loki" or "annotations"
|
||||||
const stateHistoryPrimary = config.unifiedAlerting.alertStateHistoryPrimary;
|
const stateHistoryPrimary = config.unifiedAlerting.stateHistory?.primary;
|
||||||
|
|
||||||
// if "loki" is either the backend or the primary, show the new state history implementation
|
// if "loki" is either the backend or the primary, show the new state history implementation
|
||||||
const usingNewAlertStateHistory = [stateHistoryBackend, stateHistoryPrimary].some(
|
const usingNewAlertStateHistory = [stateHistoryBackend, stateHistoryPrimary].some(
|
||||||
|
|||||||
Reference in New Issue
Block a user