|
|
|
@ -29,14 +29,12 @@ import (
|
|
|
|
|
"github.com/go-kit/kit/log/level"
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
|
|
|
|
|
|
"github.com/prometheus/prometheus/notifier"
|
|
|
|
|
"github.com/prometheus/prometheus/pkg/labels"
|
|
|
|
|
"github.com/prometheus/prometheus/pkg/rulefmt"
|
|
|
|
|
"github.com/prometheus/prometheus/pkg/timestamp"
|
|
|
|
|
"github.com/prometheus/prometheus/pkg/value"
|
|
|
|
|
"github.com/prometheus/prometheus/promql"
|
|
|
|
|
"github.com/prometheus/prometheus/storage"
|
|
|
|
|
"github.com/prometheus/prometheus/util/strutil"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Constants for instrumentation.
|
|
|
|
@ -192,7 +190,7 @@ func (g *Group) File() string { return g.file }
|
|
|
|
|
// Rules returns the group's rules.
|
|
|
|
|
func (g *Group) Rules() []Rule { return g.rules }
|
|
|
|
|
|
|
|
|
|
func (g *Group) run() {
|
|
|
|
|
func (g *Group) run(ctx context.Context) {
|
|
|
|
|
defer close(g.terminated)
|
|
|
|
|
|
|
|
|
|
// Wait an initial amount to have consistently slotted intervals.
|
|
|
|
@ -206,7 +204,7 @@ func (g *Group) run() {
|
|
|
|
|
iterationsScheduled.Inc()
|
|
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
g.Eval(start)
|
|
|
|
|
g.Eval(ctx, start)
|
|
|
|
|
|
|
|
|
|
iterationDuration.Observe(time.Since(start).Seconds())
|
|
|
|
|
g.SetEvaluationTime(time.Since(start))
|
|
|
|
@ -328,7 +326,7 @@ func typeForRule(r Rule) ruleType {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Eval runs a single evaluation cycle in which all rules are evaluated sequentially.
|
|
|
|
|
func (g *Group) Eval(ts time.Time) {
|
|
|
|
|
func (g *Group) Eval(ctx context.Context, ts time.Time) {
|
|
|
|
|
for i, rule := range g.rules {
|
|
|
|
|
select {
|
|
|
|
|
case <-g.done:
|
|
|
|
@ -346,7 +344,7 @@ func (g *Group) Eval(ts time.Time) {
|
|
|
|
|
|
|
|
|
|
evalTotal.WithLabelValues(rtyp).Inc()
|
|
|
|
|
|
|
|
|
|
vector, err := rule.Eval(g.opts.Context, ts, g.opts.Query, g.opts.ExternalURL)
|
|
|
|
|
vector, err := rule.Eval(ctx, ts, g.opts.QueryFunc, g.opts.ExternalURL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Canceled queries are intentional termination of queries. This normally
|
|
|
|
|
// happens on shutdown and thus we skip logging of any errors here.
|
|
|
|
@ -358,7 +356,7 @@ func (g *Group) Eval(ts time.Time) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ar, ok := rule.(*AlertingRule); ok {
|
|
|
|
|
g.sendAlerts(ar)
|
|
|
|
|
g.opts.NotifyFunc(ctx, ar.vector.String(), ar.currentAlerts()...)
|
|
|
|
|
}
|
|
|
|
|
var (
|
|
|
|
|
numOutOfOrder = 0
|
|
|
|
@ -418,36 +416,6 @@ func (g *Group) Eval(ts time.Time) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sendAlerts sends alert notifications for the given rule.
|
|
|
|
|
func (g *Group) sendAlerts(rule *AlertingRule) error {
|
|
|
|
|
var alerts []*notifier.Alert
|
|
|
|
|
|
|
|
|
|
for _, alert := range rule.currentAlerts() {
|
|
|
|
|
// Only send actually firing alerts.
|
|
|
|
|
if alert.State == StatePending {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a := ¬ifier.Alert{
|
|
|
|
|
StartsAt: alert.ActiveAt.Add(rule.holdDuration),
|
|
|
|
|
Labels: alert.Labels,
|
|
|
|
|
Annotations: alert.Annotations,
|
|
|
|
|
GeneratorURL: g.opts.ExternalURL.String() + strutil.TableLinkForExpression(rule.vector.String()),
|
|
|
|
|
}
|
|
|
|
|
if !alert.ResolvedAt.IsZero() {
|
|
|
|
|
a.EndsAt = alert.ResolvedAt
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
alerts = append(alerts, a)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(alerts) > 0 {
|
|
|
|
|
g.opts.Notifier.Send(alerts...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The Manager manages recording and alerting rules.
|
|
|
|
|
type Manager struct {
|
|
|
|
|
opts *ManagerOptions
|
|
|
|
@ -463,12 +431,15 @@ type Appendable interface {
|
|
|
|
|
Appender() (storage.Appender, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NotifyFunc sends notifications about a set of alerts generated by the given expression.
|
|
|
|
|
type NotifyFunc func(ctx context.Context, expr string, alerts ...*Alert) error
|
|
|
|
|
|
|
|
|
|
// ManagerOptions bundles options for the Manager.
|
|
|
|
|
type ManagerOptions struct {
|
|
|
|
|
ExternalURL *url.URL
|
|
|
|
|
Query QueryFunc
|
|
|
|
|
QueryFunc QueryFunc
|
|
|
|
|
NotifyFunc NotifyFunc
|
|
|
|
|
Context context.Context
|
|
|
|
|
Notifier *notifier.Notifier
|
|
|
|
|
Appendable Appendable
|
|
|
|
|
Logger log.Logger
|
|
|
|
|
}
|
|
|
|
@ -539,7 +510,7 @@ func (m *Manager) Update(interval time.Duration, files []string) error {
|
|
|
|
|
// is told to run. This is necessary to avoid running
|
|
|
|
|
// queries against a bootstrapping storage.
|
|
|
|
|
<-m.block
|
|
|
|
|
newg.run()
|
|
|
|
|
newg.run(m.opts.Context)
|
|
|
|
|
}()
|
|
|
|
|
wg.Done()
|
|
|
|
|
}(newg)
|
|
|
|
|