| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- package alerting
- import (
- "context"
- "fmt"
- "time"
- "github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/infra/log"
- "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/setting"
- )
- // EvalContext is the context object for an alert evaluation.
- type EvalContext struct {
- Firing bool
- IsTestRun bool
- IsDebug bool
- EvalMatches []*EvalMatch
- Logs []*ResultLogEntry
- Error error
- ConditionEvals string
- StartTime time.Time
- EndTime time.Time
- Rule *Rule
- log log.Logger
- dashboardRef *models.DashboardRef
- ImagePublicURL string
- ImageOnDiskPath string
- NoDataFound bool
- PrevAlertState models.AlertStateType
- Ctx context.Context
- }
- // NewEvalContext is the EvalContext constructor.
- func NewEvalContext(alertCtx context.Context, rule *Rule) *EvalContext {
- return &EvalContext{
- Ctx: alertCtx,
- StartTime: time.Now(),
- Rule: rule,
- Logs: make([]*ResultLogEntry, 0),
- EvalMatches: make([]*EvalMatch, 0),
- log: log.New("alerting.evalContext"),
- PrevAlertState: rule.State,
- }
- }
- // StateDescription contains visual information about the alert state.
- type StateDescription struct {
- Color string
- Text string
- Data string
- }
- // GetStateModel returns the `StateDescription` based on current state.
- func (c *EvalContext) GetStateModel() *StateDescription {
- switch c.Rule.State {
- case models.AlertStateOK:
- return &StateDescription{
- Color: "#36a64f",
- Text: "OK",
- }
- case models.AlertStateNoData:
- return &StateDescription{
- Color: "#888888",
- Text: "No Data",
- }
- case models.AlertStateAlerting:
- return &StateDescription{
- Color: "#D63232",
- Text: "Alerting",
- }
- case models.AlertStateUnknown:
- return &StateDescription{
- Color: "#888888",
- Text: "Unknown",
- }
- default:
- panic("Unknown rule state for alert " + c.Rule.State)
- }
- }
- func (c *EvalContext) shouldUpdateAlertState() bool {
- return c.Rule.State != c.PrevAlertState
- }
- // GetDurationMs returns the duration of the alert evaluation.
- func (c *EvalContext) GetDurationMs() float64 {
- return float64(c.EndTime.Nanosecond()-c.StartTime.Nanosecond()) / float64(1000000)
- }
- // GetNotificationTitle returns the title of the alert rule including alert state.
- func (c *EvalContext) GetNotificationTitle() string {
- return "[" + c.GetStateModel().Text + "] " + c.Rule.Name
- }
- // GetDashboardUID returns the dashboard uid for the alert rule.
- func (c *EvalContext) GetDashboardUID() (*models.DashboardRef, error) {
- if c.dashboardRef != nil {
- return c.dashboardRef, nil
- }
- uidQuery := &models.GetDashboardRefByIdQuery{Id: c.Rule.DashboardID}
- if err := bus.Dispatch(uidQuery); err != nil {
- return nil, err
- }
- c.dashboardRef = uidQuery.Result
- return c.dashboardRef, nil
- }
- const urlFormat = "%s?fullscreen&edit&tab=alert&panelId=%d&orgId=%d"
- // GetRuleURL returns the url to the dashboard containing the alert.
- func (c *EvalContext) GetRuleURL() (string, error) {
- if c.IsTestRun {
- return setting.AppUrl, nil
- }
- ref, err := c.GetDashboardUID()
- if err != nil {
- return "", err
- }
- return fmt.Sprintf(urlFormat, models.GetFullDashboardUrl(ref.Uid, ref.Slug), c.Rule.PanelID, c.Rule.OrgID), nil
- }
- // GetNewState returns the new state from the alert rule evaluation.
- func (c *EvalContext) GetNewState() models.AlertStateType {
- ns := getNewStateInternal(c)
- if ns != models.AlertStateAlerting || c.Rule.For == 0 {
- return ns
- }
- since := time.Since(c.Rule.LastStateChange)
- if c.PrevAlertState == models.AlertStatePending && since > c.Rule.For {
- return models.AlertStateAlerting
- }
- if c.PrevAlertState == models.AlertStateAlerting {
- return models.AlertStateAlerting
- }
- return models.AlertStatePending
- }
- func getNewStateInternal(c *EvalContext) models.AlertStateType {
- if c.Error != nil {
- c.log.Error("Alert Rule Result Error",
- "ruleId", c.Rule.ID,
- "name", c.Rule.Name,
- "error", c.Error,
- "changing state to", c.Rule.ExecutionErrorState.ToAlertState())
- if c.Rule.ExecutionErrorState == models.ExecutionErrorKeepState {
- return c.PrevAlertState
- }
- return c.Rule.ExecutionErrorState.ToAlertState()
- }
- if c.Firing {
- return models.AlertStateAlerting
- }
- if c.NoDataFound {
- c.log.Info("Alert Rule returned no data",
- "ruleId", c.Rule.ID,
- "name", c.Rule.Name,
- "changing state to", c.Rule.NoDataState.ToAlertState())
- if c.Rule.NoDataState == models.NoDataKeepState {
- return c.PrevAlertState
- }
- return c.Rule.NoDataState.ToAlertState()
- }
- return models.AlertStateOK
- }
|