// eval evaluates the rule expression and then creates pending alerts and fires // or removes previously pending alerts accordingly. func (rule *AlertingRule) eval(timestamp clientmodel.Timestamp, engine *promql.Engine) (promql.Vector, error) { query, err := engine.NewInstantQuery(rule.vector.String(), timestamp) if err != nil { return nil, err } exprResult, err := query.Exec().Vector() if err != nil { return nil, err } rule.mutex.Lock() defer rule.mutex.Unlock() // Create pending alerts for any new vector elements in the alert expression // or update the expression value for existing elements. resultFPs := map[clientmodel.Fingerprint]struct{}{} for _, sample := range exprResult { fp := sample.Metric.Metric.Fingerprint() resultFPs[fp] = struct{}{} if alert, ok := rule.activeAlerts[fp]; !ok { labels := clientmodel.LabelSet{} labels.MergeFromMetric(sample.Metric.Metric) labels = labels.Merge(rule.labels) if _, ok := labels[clientmodel.MetricNameLabel]; ok { delete(labels, clientmodel.MetricNameLabel) } rule.activeAlerts[fp] = &Alert{ Name: rule.name, Labels: labels, State: StatePending, ActiveSince: timestamp, Value: sample.Value, } } else { alert.Value = sample.Value } } vector := promql.Vector{} // Check if any pending alerts should be removed or fire now. Write out alert timeseries. for fp, activeAlert := range rule.activeAlerts { if _, ok := resultFPs[fp]; !ok { vector = append(vector, activeAlert.sample(timestamp, 0)) delete(rule.activeAlerts, fp) continue } if activeAlert.State == StatePending && timestamp.Sub(activeAlert.ActiveSince) >= rule.holdDuration { vector = append(vector, activeAlert.sample(timestamp, 0)) activeAlert.State = StateFiring } vector = append(vector, activeAlert.sample(timestamp, 1)) } return vector, nil }
// Eval evaluates the rule and then overrides the metric names and labels accordingly. func (rule RecordingRule) Eval(ctx context.Context, timestamp model.Time, engine *promql.Engine, _ string) (model.Vector, error) { query, err := engine.NewInstantQuery(rule.vector.String(), timestamp) if err != nil { return nil, err } var ( result = query.Exec(ctx) vector model.Vector ) if result.Err != nil { return nil, err } switch result.Value.(type) { case model.Vector: vector, err = result.Vector() if err != nil { return nil, err } case *model.Scalar: scalar, err := result.Scalar() if err != nil { return nil, err } vector = model.Vector{&model.Sample{ Value: scalar.Value, Timestamp: scalar.Timestamp, Metric: model.Metric{}, }} default: return nil, fmt.Errorf("rule result is not a vector or scalar") } // Override the metric name and labels. for _, sample := range vector { sample.Metric[model.MetricNameLabel] = model.LabelValue(rule.name) for label, value := range rule.labels { if value == "" { delete(sample.Metric, label) } else { sample.Metric[label] = value } } } return vector, nil }
func query(q string, timestamp clientmodel.Timestamp, queryEngine *promql.Engine) (queryResult, error) { query, err := queryEngine.NewInstantQuery(q, timestamp) if err != nil { return nil, err } res := query.Exec() if res.Err != nil { return nil, res.Err } var vector promql.Vector switch v := res.Value.(type) { case promql.Matrix: return nil, errors.New("matrix return values not supported") case promql.Vector: vector = v case *promql.Scalar: vector = promql.Vector{&promql.Sample{ Value: v.Value, Timestamp: v.Timestamp, }} case *promql.String: vector = promql.Vector{&promql.Sample{ Metric: clientmodel.COWMetric{ Metric: clientmodel.Metric{"__value__": clientmodel.LabelValue(v.Value)}, Copied: true, }, Timestamp: v.Timestamp, }} default: panic("template.query: unhandled result value type") } // promql.Vector is hard to work with in templates, so convert to // base data types. var result = make(queryResult, len(vector)) for n, v := range vector { s := sample{ Value: float64(v.Value), Labels: make(map[string]string), } for label, value := range v.Metric.Metric { s.Labels[string(label)] = string(value) } result[n] = &s } return result, nil }
// eval evaluates the rule and then overrides the metric names and labels accordingly. func (rule RecordingRule) eval(timestamp model.Time, engine *promql.Engine) (promql.Vector, error) { query, err := engine.NewInstantQuery(rule.vector.String(), timestamp) if err != nil { return nil, err } result := query.Exec() var vector promql.Vector switch result.Value.(type) { case promql.Vector: vector, err = result.Vector() if err != nil { return nil, err } case *promql.Scalar: scalar, err := result.Scalar() if err != nil { return nil, err } vector = promql.Vector{&promql.Sample{Value: scalar.Value, Timestamp: scalar.Timestamp}} default: return nil, fmt.Errorf("rule result is not a vector or scalar") } // Override the metric name and labels. for _, sample := range vector { sample.Metric.Set(model.MetricNameLabel, model.LabelValue(rule.name)) for label, value := range rule.labels { if value == "" { sample.Metric.Del(label) } else { sample.Metric.Set(label, value) } } } return vector, nil }
// eval evaluates the rule and then overrides the metric names and labels accordingly. func (rule RecordingRule) eval(timestamp clientmodel.Timestamp, engine *promql.Engine) (promql.Vector, error) { query, err := engine.NewInstantQuery(rule.vector.String(), timestamp) if err != nil { return nil, err } vector, err := query.Exec().Vector() if err != nil { return nil, err } // Override the metric name and labels. for _, sample := range vector { sample.Metric.Set(clientmodel.MetricNameLabel, clientmodel.LabelValue(rule.name)) for label, value := range rule.labels { if value == "" { sample.Metric.Delete(label) } else { sample.Metric.Set(label, value) } } } return vector, nil }
// eval evaluates the rule expression and then creates pending alerts and fires // or removes previously pending alerts accordingly. func (r *AlertingRule) eval(ts model.Time, engine *promql.Engine) (model.Vector, error) { query, err := engine.NewInstantQuery(r.vector.String(), ts) if err != nil { return nil, err } res, err := query.Exec().Vector() if err != nil { return nil, err } r.mtx.Lock() defer r.mtx.Unlock() // Create pending alerts for any new vector elements in the alert expression // or update the expression value for existing elements. resultFPs := map[model.Fingerprint]struct{}{} for _, smpl := range res { fp := smpl.Metric.Fingerprint() resultFPs[fp] = struct{}{} if alert, ok := r.active[fp]; ok { alert.Value = smpl.Value continue } delete(smpl.Metric, model.MetricNameLabel) r.active[fp] = &Alert{ Labels: model.LabelSet(smpl.Metric), ActiveAt: ts, State: StatePending, Value: smpl.Value, } } var vec model.Vector // Check if any pending alerts should be removed or fire now. Write out alert timeseries. for fp, a := range r.active { if _, ok := resultFPs[fp]; !ok { if a.State != StateInactive { vec = append(vec, r.sample(a, ts, false)) } // If the alert was previously firing, keep it around for a given // retention time so it is reported as resolved to the AlertManager. if a.State == StatePending || (a.ResolvedAt != 0 && ts.Sub(a.ResolvedAt) > resolvedRetention) { delete(r.active, fp) } if a.State != StateInactive { a.State = StateInactive a.ResolvedAt = ts } continue } if a.State == StatePending && ts.Sub(a.ActiveAt) >= r.holdDuration { vec = append(vec, r.sample(a, ts, false)) a.State = StateFiring } vec = append(vec, r.sample(a, ts, true)) } return vec, nil }
// eval evaluates the rule expression and then creates pending alerts and fires // or removes previously pending alerts accordingly. func (r *AlertingRule) eval(ts model.Time, engine *promql.Engine, externalURLPath string) (model.Vector, error) { query, err := engine.NewInstantQuery(r.vector.String(), ts) if err != nil { return nil, err } res, err := query.Exec().Vector() if err != nil { return nil, err } r.mtx.Lock() defer r.mtx.Unlock() // Create pending alerts for any new vector elements in the alert expression // or update the expression value for existing elements. resultFPs := map[model.Fingerprint]struct{}{} for _, smpl := range res { // Provide the alert information to the template. l := make(map[string]string, len(smpl.Metric)) for k, v := range smpl.Metric { l[string(k)] = string(v) } tmplData := struct { Labels map[string]string Value float64 }{ Labels: l, Value: float64(smpl.Value), } // Inject some convenience variables that are easier to remember for users // who are not used to Go's templating system. defs := "{{$labels := .Labels}}{{$value := .Value}}" expand := func(text model.LabelValue) model.LabelValue { tmpl := template.NewTemplateExpander( defs+string(text), "__alert_"+r.Name(), tmplData, ts, engine, externalURLPath, ) result, err := tmpl.Expand() if err != nil { result = fmt.Sprintf("<error expanding template: %s>", err) log.Warnf("Error expanding alert template %v with data '%v': %s", r.Name(), tmplData, err) } return model.LabelValue(result) } labels := make(model.LabelSet, len(smpl.Metric)+len(r.labels)+1) for ln, lv := range smpl.Metric { labels[ln] = lv } for ln, lv := range r.labels { labels[ln] = expand(lv) } labels[model.AlertNameLabel] = model.LabelValue(r.Name()) annotations := make(model.LabelSet, len(r.annotations)) for an, av := range r.annotations { annotations[an] = expand(av) } fp := smpl.Metric.Fingerprint() resultFPs[fp] = struct{}{} if alert, ok := r.active[fp]; ok && alert.State != StateInactive { alert.Value = smpl.Value continue } delete(smpl.Metric, model.MetricNameLabel) r.active[fp] = &Alert{ Labels: labels, Annotations: annotations, ActiveAt: ts, State: StatePending, Value: smpl.Value, } } var vec model.Vector // Check if any pending alerts should be removed or fire now. Write out alert timeseries. for fp, a := range r.active { if _, ok := resultFPs[fp]; !ok { if a.State != StateInactive { vec = append(vec, r.sample(a, ts, false)) } // If the alert was previously firing, keep it around for a given // retention time so it is reported as resolved to the AlertManager. if a.State == StatePending || (a.ResolvedAt != 0 && ts.Sub(a.ResolvedAt) > resolvedRetention) { delete(r.active, fp) } if a.State != StateInactive { a.State = StateInactive a.ResolvedAt = ts } continue } if a.State == StatePending && ts.Sub(a.ActiveAt) >= r.holdDuration { vec = append(vec, r.sample(a, ts, false)) a.State = StateFiring } vec = append(vec, r.sample(a, ts, true)) } return vec, nil }