mirror of
https://github.com/grafana/grafana.git
synced 2025-01-16 19:52:33 -06:00
alerting: fix a bunch of lint issues. (#17128)
This commit is contained in:
parent
a564a54b13
commit
bd5bcea5d0
@ -17,44 +17,43 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// AlertingService is the background process that
|
||||
// schedules alert evaluations and makes sure notifications
|
||||
// are sent.
|
||||
type AlertingService struct {
|
||||
RenderService rendering.Service `inject:""`
|
||||
|
||||
execQueue chan *Job
|
||||
//clock clock.Clock
|
||||
execQueue chan *Job
|
||||
ticker *Ticker
|
||||
scheduler Scheduler
|
||||
evalHandler EvalHandler
|
||||
ruleReader RuleReader
|
||||
scheduler scheduler
|
||||
evalHandler evalHandler
|
||||
ruleReader ruleReader
|
||||
log log.Logger
|
||||
resultHandler ResultHandler
|
||||
resultHandler resultHandler
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterService(&AlertingService{})
|
||||
}
|
||||
|
||||
func NewEngine() *AlertingService {
|
||||
e := &AlertingService{}
|
||||
e.Init()
|
||||
return e
|
||||
}
|
||||
|
||||
// IsDisabled returns true if the alerting service is disable for this instance.
|
||||
func (e *AlertingService) IsDisabled() bool {
|
||||
return !setting.AlertingEnabled || !setting.ExecuteAlerts
|
||||
}
|
||||
|
||||
// Init initalizes the AlertingService.
|
||||
func (e *AlertingService) Init() error {
|
||||
e.ticker = NewTicker(time.Now(), time.Second*0, clock.New())
|
||||
e.execQueue = make(chan *Job, 1000)
|
||||
e.scheduler = NewScheduler()
|
||||
e.scheduler = newScheduler()
|
||||
e.evalHandler = NewEvalHandler()
|
||||
e.ruleReader = NewRuleReader()
|
||||
e.ruleReader = newRuleReader()
|
||||
e.log = log.New("alerting.engine")
|
||||
e.resultHandler = NewResultHandler(e.RenderService)
|
||||
e.resultHandler = newResultHandler(e.RenderService)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run starts the alerting service background process.
|
||||
func (e *AlertingService) Run(ctx context.Context) error {
|
||||
alertGroup, ctx := errgroup.WithContext(ctx)
|
||||
alertGroup.Go(func() error { return e.alertingTicker(ctx) })
|
||||
@ -80,7 +79,7 @@ func (e *AlertingService) alertingTicker(grafanaCtx context.Context) error {
|
||||
case tick := <-e.ticker.C:
|
||||
// TEMP SOLUTION update rules ever tenth tick
|
||||
if tickIndex%10 == 0 {
|
||||
e.scheduler.Update(e.ruleReader.Fetch())
|
||||
e.scheduler.Update(e.ruleReader.fetch())
|
||||
}
|
||||
|
||||
e.scheduler.Tick(tick, e.execQueue)
|
||||
@ -211,7 +210,7 @@ func (e *AlertingService) processJob(attemptID int, attemptChan chan int, cancel
|
||||
// dont reuse the evalContext and get its own context.
|
||||
evalContext.Ctx = resultHandleCtx
|
||||
evalContext.Rule.State = evalContext.GetNewState()
|
||||
e.resultHandler.Handle(evalContext)
|
||||
e.resultHandler.handle(evalContext)
|
||||
span.Finish()
|
||||
e.log.Debug("Job Execution completed", "timeMs", evalContext.GetDurationMs(), "alertId", evalContext.Rule.Id, "name", evalContext.Rule.Name, "firing", evalContext.Firing, "attemptID", attemptID)
|
||||
close(attemptChan)
|
||||
|
@ -17,7 +17,8 @@ import (
|
||||
|
||||
func TestEngineTimeouts(t *testing.T) {
|
||||
Convey("Alerting engine timeout tests", t, func() {
|
||||
engine := NewEngine()
|
||||
engine := &AlertingService{}
|
||||
engine.Init()
|
||||
setting.AlertingNotificationTimeout = 30 * time.Second
|
||||
setting.AlertingMaxAttempts = 3
|
||||
engine.resultHandler = &FakeResultHandler{}
|
||||
@ -89,7 +90,7 @@ func (handler *FakeCommonTimeoutHandler) Eval(evalContext *EvalContext) {
|
||||
evalContext.Error = errors.New("Fake evaluation timeout test failure; wrong response")
|
||||
}
|
||||
|
||||
func (handler *FakeCommonTimeoutHandler) Handle(evalContext *EvalContext) error {
|
||||
func (handler *FakeCommonTimeoutHandler) handle(evalContext *EvalContext) error {
|
||||
// 1. prepare mock server
|
||||
path := "/resulthandle"
|
||||
srv := runBusyServer(path, handler.ServerBusySleepDuration)
|
||||
|
@ -6,9 +6,10 @@ import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FakeEvalHandler struct {
|
||||
@ -32,13 +33,14 @@ func (handler *FakeEvalHandler) Eval(evalContext *EvalContext) {
|
||||
|
||||
type FakeResultHandler struct{}
|
||||
|
||||
func (handler *FakeResultHandler) Handle(evalContext *EvalContext) error {
|
||||
func (handler *FakeResultHandler) handle(evalContext *EvalContext) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestEngineProcessJob(t *testing.T) {
|
||||
Convey("Alerting engine job processing", t, func() {
|
||||
engine := NewEngine()
|
||||
engine := &AlertingService{}
|
||||
engine.Init()
|
||||
setting.AlertingEvaluationTimeout = 30 * time.Second
|
||||
setting.AlertingNotificationTimeout = 30 * time.Second
|
||||
setting.AlertingMaxAttempts = 3
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// EvalContext is the context object for an alert evaluation.
|
||||
type EvalContext struct {
|
||||
Firing bool
|
||||
IsTestRun bool
|
||||
@ -33,6 +34,7 @@ type EvalContext struct {
|
||||
Ctx context.Context
|
||||
}
|
||||
|
||||
// NewEvalContext is the EvalContext constructor.
|
||||
func NewEvalContext(alertCtx context.Context, rule *Rule) *EvalContext {
|
||||
return &EvalContext{
|
||||
Ctx: alertCtx,
|
||||
@ -45,12 +47,14 @@ func NewEvalContext(alertCtx context.Context, rule *Rule) *EvalContext {
|
||||
}
|
||||
}
|
||||
|
||||
// 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:
|
||||
@ -78,18 +82,21 @@ func (c *EvalContext) GetStateModel() *StateDescription {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *EvalContext) ShouldUpdateAlertState() bool {
|
||||
func (c *EvalContext) shouldUpdateAlertState() bool {
|
||||
return c.Rule.State != c.PrevAlertState
|
||||
}
|
||||
|
||||
func (a *EvalContext) GetDurationMs() float64 {
|
||||
return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000)
|
||||
// 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
|
||||
@ -106,6 +113,7 @@ func (c *EvalContext) GetDashboardUID() (*models.DashboardRef, error) {
|
||||
|
||||
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
|
||||
@ -118,7 +126,7 @@ func (c *EvalContext) GetRuleUrl() (string, error) {
|
||||
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
|
||||
// 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 {
|
||||
|
@ -18,7 +18,7 @@ func TestStateIsUpdatedWhenNeeded(t *testing.T) {
|
||||
ctx.PrevAlertState = models.AlertStateOK
|
||||
ctx.Rule.State = models.AlertStateAlerting
|
||||
|
||||
if !ctx.ShouldUpdateAlertState() {
|
||||
if !ctx.shouldUpdateAlertState() {
|
||||
t.Fatalf("expected should updated to be true")
|
||||
}
|
||||
})
|
||||
@ -27,7 +27,7 @@ func TestStateIsUpdatedWhenNeeded(t *testing.T) {
|
||||
ctx.PrevAlertState = models.AlertStateOK
|
||||
ctx.Rule.State = models.AlertStateOK
|
||||
|
||||
if ctx.ShouldUpdateAlertState() {
|
||||
if ctx.shouldUpdateAlertState() {
|
||||
t.Fatalf("expected should updated to be false")
|
||||
}
|
||||
})
|
||||
|
@ -9,11 +9,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
)
|
||||
|
||||
// DefaultEvalHandler is responsible for evaluating the alert rule.
|
||||
type DefaultEvalHandler struct {
|
||||
log log.Logger
|
||||
alertJobTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewEvalHandler is the `DefaultEvalHandler` constructor.
|
||||
func NewEvalHandler() *DefaultEvalHandler {
|
||||
return &DefaultEvalHandler{
|
||||
log: log.New("alerting.evalHandler"),
|
||||
@ -21,6 +23,7 @@ func NewEvalHandler() *DefaultEvalHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// Eval evaluated the alert rule.
|
||||
func (e *DefaultEvalHandler) Eval(context *EvalContext) {
|
||||
firing := true
|
||||
noDataFound := true
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
// DashAlertExtractor extracts alerts from the dashboard json
|
||||
// DashAlertExtractor extracts alerts from the dashboard json.
|
||||
type DashAlertExtractor struct {
|
||||
User *models.SignedInUser
|
||||
Dash *models.Dashboard
|
||||
@ -19,7 +19,7 @@ type DashAlertExtractor struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// NewDashAlertExtractor returns a new DashAlertExtractor
|
||||
// NewDashAlertExtractor returns a new DashAlertExtractor.
|
||||
func NewDashAlertExtractor(dash *models.Dashboard, orgID int64, user *models.SignedInUser) *DashAlertExtractor {
|
||||
return &DashAlertExtractor{
|
||||
User: user,
|
||||
@ -207,7 +207,7 @@ func validateAlertRule(alert *models.Alert) bool {
|
||||
return alert.ValidToSave()
|
||||
}
|
||||
|
||||
// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data
|
||||
// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data.
|
||||
func (e *DashAlertExtractor) GetAlerts() ([]*models.Alert, error) {
|
||||
return e.extractAlerts(validateAlertRule)
|
||||
}
|
||||
@ -247,7 +247,7 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert
|
||||
}
|
||||
|
||||
// ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id
|
||||
// in the first validation pass
|
||||
// in the first validation pass.
|
||||
func (e *DashAlertExtractor) ValidateAlerts() error {
|
||||
_, err := e.extractAlerts(func(alert *models.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 })
|
||||
return err
|
||||
|
@ -52,10 +52,10 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
Convey("Extractor should not modify the original json", func() {
|
||||
dashJson, err := simplejson.NewJson(json)
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
|
||||
getTarget := func(j *simplejson.Json) string {
|
||||
rowObj := j.Get("rows").MustArray()[0]
|
||||
@ -68,23 +68,23 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
}
|
||||
|
||||
Convey("Dashboard json rows.panels.alert.query.model.target should be empty", func() {
|
||||
So(getTarget(dashJson), ShouldEqual, "")
|
||||
So(getTarget(dashJSON), ShouldEqual, "")
|
||||
})
|
||||
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
_, _ = extractor.GetAlerts()
|
||||
|
||||
Convey("Dashboard json should not be updated after extracting rules", func() {
|
||||
So(getTarget(dashJson), ShouldEqual, "")
|
||||
So(getTarget(dashJSON), ShouldEqual, "")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Parsing and validating dashboard containing graphite alerts", func() {
|
||||
|
||||
dashJson, err := simplejson.NewJson(json)
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts()
|
||||
@ -147,12 +147,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Panels missing id should return error", func() {
|
||||
panelWithoutId, err := ioutil.ReadFile("./testdata/panels-missing-id.json")
|
||||
panelWithoutID, err := ioutil.ReadFile("./testdata/panels-missing-id.json")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dashJson, err := simplejson.NewJson(panelWithoutId)
|
||||
dashJSON, err := simplejson.NewJson(panelWithoutID)
|
||||
So(err, ShouldBeNil)
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
_, err = extractor.GetAlerts()
|
||||
@ -163,12 +163,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Panel with id set to zero should return error", func() {
|
||||
panelWithIdZero, err := ioutil.ReadFile("./testdata/panel-with-id-0.json")
|
||||
panelWithIDZero, err := ioutil.ReadFile("./testdata/panel-with-id-0.json")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dashJson, err := simplejson.NewJson(panelWithIdZero)
|
||||
dashJSON, err := simplejson.NewJson(panelWithIDZero)
|
||||
So(err, ShouldBeNil)
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
_, err = extractor.GetAlerts()
|
||||
@ -182,9 +182,9 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
json, err := ioutil.ReadFile("./testdata/v5-dashboard.json")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dashJson, err := simplejson.NewJson(json)
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
So(err, ShouldBeNil)
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts()
|
||||
@ -211,9 +211,9 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
json, err := ioutil.ReadFile("./testdata/influxdb-alert.json")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dashJson, err := simplejson.NewJson(json)
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
So(err, ShouldBeNil)
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts()
|
||||
@ -240,10 +240,10 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
json, err := ioutil.ReadFile("./testdata/collapsed-panels.json")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dashJson, err := simplejson.NewJson(json)
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dash := models.NewDashboardFromJson(dashJson)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts()
|
||||
|
@ -7,15 +7,16 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type EvalHandler interface {
|
||||
type evalHandler interface {
|
||||
Eval(evalContext *EvalContext)
|
||||
}
|
||||
|
||||
type Scheduler interface {
|
||||
type scheduler interface {
|
||||
Tick(time time.Time, execQueue chan *Job)
|
||||
Update(rules []*Rule)
|
||||
}
|
||||
|
||||
// Notifier is responsible for sending alert notifications.
|
||||
type Notifier interface {
|
||||
Notify(evalContext *EvalContext) error
|
||||
GetType() string
|
||||
@ -48,6 +49,7 @@ func (notifiers notifierStateSlice) ShouldUploadImage() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ConditionResult is the result of a condition evaluation.
|
||||
type ConditionResult struct {
|
||||
Firing bool
|
||||
NoDataFound bool
|
||||
@ -55,6 +57,7 @@ type ConditionResult struct {
|
||||
EvalMatches []*EvalMatch
|
||||
}
|
||||
|
||||
// Condition is responsible for evaluating an alert condition.
|
||||
type Condition interface {
|
||||
Eval(result *EvalContext) (*ConditionResult, error)
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package alerting
|
||||
|
||||
import "github.com/grafana/grafana/pkg/components/null"
|
||||
|
||||
// Job holds state about when the alert rule should
|
||||
// be evaluated.
|
||||
type Job struct {
|
||||
Offset int64
|
||||
OffsetWait bool
|
||||
@ -10,18 +12,15 @@ type Job struct {
|
||||
Rule *Rule
|
||||
}
|
||||
|
||||
// ResultLogEntry represents log data for the alert evaluation.
|
||||
type ResultLogEntry struct {
|
||||
Message string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// EvalMatch represents the serie violating the threshold.
|
||||
type EvalMatch struct {
|
||||
Value null.Float `json:"value"`
|
||||
Metric string `json:"metric"`
|
||||
Tags map[string]string `json:"tags"`
|
||||
}
|
||||
|
||||
type Level struct {
|
||||
Operator string
|
||||
Value float64
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// NotifierPlugin holds meta information about a notifier.
|
||||
type NotifierPlugin struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
@ -21,11 +22,7 @@ type NotifierPlugin struct {
|
||||
Factory NotifierFactory `json:"-"`
|
||||
}
|
||||
|
||||
type NotificationService interface {
|
||||
SendIfNeeded(context *EvalContext) error
|
||||
}
|
||||
|
||||
func NewNotificationService(renderService rendering.Service) NotificationService {
|
||||
func newNotificationService(renderService rendering.Service) *notificationService {
|
||||
return ¬ificationService{
|
||||
log: log.New("alerting.notifier"),
|
||||
renderService: renderService,
|
||||
@ -156,8 +153,8 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids []string, evalContext *EvalContext) (notifierStateSlice, error) {
|
||||
query := &models.GetAlertNotificationsWithUidToSendQuery{OrgId: orgId, Uids: notificationUids}
|
||||
func (n *notificationService) getNeededNotifiers(orgID int64, notificationUids []string, evalContext *EvalContext) (notifierStateSlice, error) {
|
||||
query := &models.GetAlertNotificationsWithUidToSendQuery{OrgId: orgID, Uids: notificationUids}
|
||||
|
||||
if err := bus.Dispatch(query); err != nil {
|
||||
return nil, err
|
||||
@ -194,7 +191,7 @@ func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids [
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// InitNotifier instantiate a new notifier based on the model
|
||||
// InitNotifier instantiate a new notifier based on the model.
|
||||
func InitNotifier(model *models.AlertNotification) (Notifier, error) {
|
||||
notifierPlugin, found := notifierFactories[model.Type]
|
||||
if !found {
|
||||
@ -204,6 +201,7 @@ func InitNotifier(model *models.AlertNotification) (Notifier, error) {
|
||||
return notifierPlugin.Factory(model)
|
||||
}
|
||||
|
||||
// NotifierFactory is a signature for creating notifiers.
|
||||
type NotifierFactory func(notification *models.AlertNotification) (Notifier, error)
|
||||
|
||||
var notifierFactories = make(map[string]*NotifierPlugin)
|
||||
@ -213,6 +211,7 @@ func RegisterNotifier(plugin *NotifierPlugin) {
|
||||
notifierFactories[plugin.Type] = plugin
|
||||
}
|
||||
|
||||
// GetNotifiers returns a list of metadata about available notifiers.
|
||||
func GetNotifiers() []*NotifierPlugin {
|
||||
list := make([]*NotifierPlugin, 0)
|
||||
|
||||
|
@ -2,7 +2,6 @@ package alerting
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@ -10,35 +9,24 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type RuleReader interface {
|
||||
Fetch() []*Rule
|
||||
type ruleReader interface {
|
||||
fetch() []*Rule
|
||||
}
|
||||
|
||||
type DefaultRuleReader struct {
|
||||
type defaultRuleReader struct {
|
||||
sync.RWMutex
|
||||
serverPosition int
|
||||
clusterSize int
|
||||
log log.Logger
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewRuleReader() *DefaultRuleReader {
|
||||
ruleReader := &DefaultRuleReader{
|
||||
func newRuleReader() *defaultRuleReader {
|
||||
ruleReader := &defaultRuleReader{
|
||||
log: log.New("alerting.ruleReader"),
|
||||
}
|
||||
|
||||
go ruleReader.initReader()
|
||||
return ruleReader
|
||||
}
|
||||
|
||||
func (arr *DefaultRuleReader) initReader() {
|
||||
heartbeat := time.NewTicker(time.Second * 10)
|
||||
|
||||
for range heartbeat.C {
|
||||
arr.heartbeat()
|
||||
}
|
||||
}
|
||||
|
||||
func (arr *DefaultRuleReader) Fetch() []*Rule {
|
||||
func (arr *defaultRuleReader) fetch() []*Rule {
|
||||
cmd := &models.GetAllAlertsQuery{}
|
||||
|
||||
if err := bus.Dispatch(cmd); err != nil {
|
||||
@ -58,8 +46,3 @@ func (arr *DefaultRuleReader) Fetch() []*Rule {
|
||||
metrics.M_Alerting_Active_Alerts.Set(float64(len(res)))
|
||||
return res
|
||||
}
|
||||
|
||||
func (arr *DefaultRuleReader) heartbeat() {
|
||||
arr.clusterSize = 1
|
||||
arr.serverPosition = 1
|
||||
}
|
||||
|
@ -13,23 +13,23 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
)
|
||||
|
||||
type ResultHandler interface {
|
||||
Handle(evalContext *EvalContext) error
|
||||
type resultHandler interface {
|
||||
handle(evalContext *EvalContext) error
|
||||
}
|
||||
|
||||
type DefaultResultHandler struct {
|
||||
notifier NotificationService
|
||||
type defaultResultHandler struct {
|
||||
notifier *notificationService
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewResultHandler(renderService rendering.Service) *DefaultResultHandler {
|
||||
return &DefaultResultHandler{
|
||||
func newResultHandler(renderService rendering.Service) *defaultResultHandler {
|
||||
return &defaultResultHandler{
|
||||
log: log.New("alerting.resultHandler"),
|
||||
notifier: NewNotificationService(renderService),
|
||||
notifier: newNotificationService(renderService),
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
|
||||
func (handler *defaultResultHandler) handle(evalContext *EvalContext) error {
|
||||
executionError := ""
|
||||
annotationData := simplejson.New()
|
||||
|
||||
@ -45,7 +45,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
|
||||
}
|
||||
|
||||
metrics.M_Alerting_Result_State.WithLabelValues(string(evalContext.Rule.State)).Inc()
|
||||
if evalContext.ShouldUpdateAlertState() {
|
||||
if evalContext.shouldUpdateAlertState() {
|
||||
handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "prev state", evalContext.PrevAlertState)
|
||||
|
||||
cmd := &models.SetAlertStateCommand{
|
||||
|
@ -12,10 +12,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrFrequencyCannotBeZeroOrLess frequency cannot be below zero
|
||||
ErrFrequencyCannotBeZeroOrLess = errors.New(`"evaluate every" cannot be zero or below`)
|
||||
ErrFrequencyCouldNotBeParsed = errors.New(`"evaluate every" field could not be parsed`)
|
||||
|
||||
// ErrFrequencyCouldNotBeParsed frequency cannot be parsed
|
||||
ErrFrequencyCouldNotBeParsed = errors.New(`"evaluate every" field could not be parsed`)
|
||||
)
|
||||
|
||||
// Rule is the in-memory version of an alert rule.
|
||||
type Rule struct {
|
||||
Id int64
|
||||
OrgId int64
|
||||
@ -35,6 +39,8 @@ type Rule struct {
|
||||
StateChanges int64
|
||||
}
|
||||
|
||||
// ValidationError is a typed error with meta data
|
||||
// about the validation error.
|
||||
type ValidationError struct {
|
||||
Reason string
|
||||
Err error
|
||||
@ -65,8 +71,8 @@ func (e ValidationError) Error() string {
|
||||
}
|
||||
|
||||
var (
|
||||
ValueFormatRegex = regexp.MustCompile(`^\d+`)
|
||||
UnitFormatRegex = regexp.MustCompile(`\w{1}$`)
|
||||
valueFormatRegex = regexp.MustCompile(`^\d+`)
|
||||
unitFormatRegex = regexp.MustCompile(`\w{1}$`)
|
||||
)
|
||||
|
||||
var unitMultiplier = map[string]int{
|
||||
@ -79,7 +85,7 @@ var unitMultiplier = map[string]int{
|
||||
func getTimeDurationStringToSeconds(str string) (int64, error) {
|
||||
multiplier := 1
|
||||
|
||||
matches := ValueFormatRegex.FindAllString(str, 1)
|
||||
matches := valueFormatRegex.FindAllString(str, 1)
|
||||
|
||||
if len(matches) <= 0 {
|
||||
return 0, ErrFrequencyCouldNotBeParsed
|
||||
@ -94,7 +100,7 @@ func getTimeDurationStringToSeconds(str string) (int64, error) {
|
||||
return 0, ErrFrequencyCannotBeZeroOrLess
|
||||
}
|
||||
|
||||
unit := UnitFormatRegex.FindAllString(str, 1)[0]
|
||||
unit := unitFormatRegex.FindAllString(str, 1)[0]
|
||||
|
||||
if val, ok := unitMultiplier[unit]; ok {
|
||||
multiplier = val
|
||||
@ -103,6 +109,8 @@ func getTimeDurationStringToSeconds(str string) (int64, error) {
|
||||
return int64(value * multiplier), nil
|
||||
}
|
||||
|
||||
// NewRuleFromDBAlert mappes an db version of
|
||||
// alert to an in-memory version.
|
||||
func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) {
|
||||
model := &Rule{}
|
||||
model.Id = ruleDef.Id
|
||||
@ -159,10 +167,12 @@ func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) {
|
||||
return model, nil
|
||||
}
|
||||
|
||||
// ConditionFactory is the function signature for creating `Conditions`.
|
||||
type ConditionFactory func(model *simplejson.Json, index int) (Condition, error)
|
||||
|
||||
var conditionFactories = make(map[string]ConditionFactory)
|
||||
|
||||
// RegisterCondition adds support for alerting conditions.
|
||||
func RegisterCondition(typeName string, factory ConditionFactory) {
|
||||
conditionFactories[typeName] = factory
|
||||
}
|
||||
|
@ -8,19 +8,19 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type SchedulerImpl struct {
|
||||
type schedulerImpl struct {
|
||||
jobs map[int64]*Job
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func NewScheduler() Scheduler {
|
||||
return &SchedulerImpl{
|
||||
func newScheduler() scheduler {
|
||||
return &schedulerImpl{
|
||||
jobs: make(map[int64]*Job),
|
||||
log: log.New("alerting.scheduler"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchedulerImpl) Update(rules []*Rule) {
|
||||
func (s *schedulerImpl) Update(rules []*Rule) {
|
||||
s.log.Debug("Scheduling update", "ruleCount", len(rules))
|
||||
|
||||
jobs := make(map[int64]*Job)
|
||||
@ -48,7 +48,7 @@ func (s *SchedulerImpl) Update(rules []*Rule) {
|
||||
s.jobs = jobs
|
||||
}
|
||||
|
||||
func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) {
|
||||
func (s *schedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) {
|
||||
now := tickTime.Unix()
|
||||
|
||||
for _, job := range s.jobs {
|
||||
@ -72,7 +72,7 @@ func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchedulerImpl) enqueue(job *Job, execQueue chan *Job) {
|
||||
func (s *schedulerImpl) enqueue(job *Job, execQueue chan *Job) {
|
||||
s.log.Debug("Scheduler: Putting job on to exec queue", "name", job.Rule.Name, "id", job.Rule.Id)
|
||||
execQueue <- job
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
// NotificationTestCommand initiates an test
|
||||
// execution of an alert notification.
|
||||
type NotificationTestCommand struct {
|
||||
State models.AlertStateType
|
||||
Name string
|
||||
@ -27,7 +29,7 @@ func init() {
|
||||
}
|
||||
|
||||
func handleNotificationTestCommand(cmd *NotificationTestCommand) error {
|
||||
notifier := NewNotificationService(nil).(*notificationService)
|
||||
notifier := newNotificationService(nil)
|
||||
|
||||
model := &models.AlertNotification{
|
||||
Name: cmd.Name,
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
// AlertTestCommand initiates an test evaluation
|
||||
// of an alert rule.
|
||||
type AlertTestCommand struct {
|
||||
Dashboard *simplejson.Json
|
||||
PanelId int64
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"github.com/benbjohnson/clock"
|
||||
)
|
||||
|
||||
// ticker is a ticker to power the alerting scheduler. it's like a time.Ticker, except:
|
||||
// Ticker is a ticker to power the alerting scheduler. it's like a time.Ticker, except:
|
||||
// * it doesn't drop ticks for slow receivers, rather, it queues up. so that callers are in control to instrument what's going on.
|
||||
// * it automatically ticks every second, which is the right thing in our current design
|
||||
// * it ticks on second marks or very shortly after. this provides a predictable load pattern
|
||||
|
Loading…
Reference in New Issue
Block a user