2015-01-21 20:07:45 +01:00
// Copyright 2013 The Prometheus Authors
2013-04-24 11:51:40 +02:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rules
import (
2017-10-24 21:21:42 -07:00
"context"
2013-04-24 11:51:40 +02:00
"fmt"
2013-06-13 16:10:05 +02:00
"html/template"
2017-05-13 15:47:04 +02:00
"net/url"
2017-11-17 15:18:34 +00:00
"sync"
2016-12-29 17:31:14 +01:00
"time"
2013-06-13 16:10:05 +02:00
2017-07-08 11:38:02 +02:00
yaml "gopkg.in/yaml.v2"
2016-12-25 00:37:46 +01:00
"github.com/prometheus/prometheus/pkg/labels"
2017-07-08 11:38:02 +02:00
"github.com/prometheus/prometheus/pkg/rulefmt"
2015-03-30 19:43:19 +02:00
"github.com/prometheus/prometheus/promql"
2020-02-03 19:06:39 +01:00
"github.com/prometheus/prometheus/promql/parser"
2015-05-29 13:30:30 +02:00
"github.com/prometheus/prometheus/util/strutil"
2013-04-24 11:51:40 +02:00
)
// A RecordingRule records its vector expression into new timeseries.
type RecordingRule struct {
2018-08-06 18:33:45 -04:00
name string
2020-02-03 19:06:39 +01:00
vector parser . Expr
2018-08-06 18:33:45 -04:00
labels labels . Labels
// Protects the below.
mtx sync . Mutex
// The health of the recording rule.
health RuleHealth
2018-10-12 12:26:59 -04:00
// Timestamp of last evaluation of the recording rule.
evaluationTimestamp time . Time
2018-08-06 18:33:45 -04:00
// The last error seen by the recording rule.
2018-10-12 12:26:59 -04:00
lastError error
// Duration of how long it took to evaluate the recording rule.
2018-07-18 05:54:33 +02:00
evaluationDuration time . Duration
2013-04-24 11:51:40 +02:00
}
2015-05-25 21:16:32 +02:00
// NewRecordingRule returns a new recording rule.
2020-02-03 19:06:39 +01:00
func NewRecordingRule ( name string , vector parser . Expr , lset labels . Labels ) * RecordingRule {
2015-05-25 21:16:32 +02:00
return & RecordingRule {
name : name ,
vector : vector ,
2018-08-06 18:33:45 -04:00
health : HealthUnknown ,
2016-12-25 00:37:46 +01:00
labels : lset ,
2015-05-25 21:16:32 +02:00
}
}
2014-12-10 16:16:49 +01:00
// Name returns the rule name.
2017-11-17 15:18:34 +00:00
func ( rule * RecordingRule ) Name ( ) string {
2015-12-14 17:40:40 +01:00
return rule . name
}
2013-04-24 11:51:40 +02:00
2018-06-27 15:15:17 +08:00
// Query returns the rule query expression.
2020-02-03 19:06:39 +01:00
func ( rule * RecordingRule ) Query ( ) parser . Expr {
2018-06-27 15:15:17 +08:00
return rule . vector
}
// Labels returns the rule labels.
func ( rule * RecordingRule ) Labels ( ) labels . Labels {
return rule . labels
}
2016-11-18 16:12:50 +00:00
// Eval evaluates the rule and then overrides the metric names and labels accordingly.
2017-11-23 13:04:54 +01:00
func ( rule * RecordingRule ) Eval ( ctx context . Context , ts time . Time , query QueryFunc , _ * url . URL ) ( promql . Vector , error ) {
vector , err := query ( ctx , rule . vector . String ( ) , ts )
2015-03-30 19:43:19 +02:00
if err != nil {
return nil , err
}
2013-04-24 11:51:40 +02:00
// Override the metric name and labels.
2016-12-29 17:31:14 +01:00
for i := range vector {
sample := & vector [ i ]
2016-12-25 00:37:46 +01:00
lb := labels . NewBuilder ( sample . Metric )
2015-08-24 18:04:41 +02:00
2016-12-25 00:37:46 +01:00
lb . Set ( labels . MetricName , rule . name )
for _ , l := range rule . labels {
2019-08-13 11:19:17 +01:00
lb . Set ( l . Name , l . Value )
2013-04-24 11:51:40 +02:00
}
2016-12-25 00:37:46 +01:00
sample . Metric = lb . Labels ( )
2013-04-24 11:51:40 +02:00
}
2019-12-18 13:29:35 +01:00
// Check that the rule does not produce identical metrics after applying
// labels.
if vector . ContainsSameLabelset ( ) {
err = fmt . Errorf ( "vector contains metrics with the same labelset after applying rule labels" )
rule . SetHealth ( HealthBad )
rule . SetLastError ( err )
return nil , err
}
2018-08-06 18:33:45 -04:00
rule . SetHealth ( HealthGood )
rule . SetLastError ( err )
2013-05-16 08:38:31 +03:00
return vector , nil
2013-04-24 11:51:40 +02:00
}
2017-11-17 15:18:34 +00:00
func ( rule * RecordingRule ) String ( ) string {
2017-07-08 11:38:02 +02:00
r := rulefmt . Rule {
Record : rule . name ,
Expr : rule . vector . String ( ) ,
Labels : rule . labels . Map ( ) ,
}
byt , err := yaml . Marshal ( r )
if err != nil {
2018-11-27 17:44:29 +01:00
return fmt . Sprintf ( "error marshaling recording rule: %q" , err . Error ( ) )
2017-07-08 11:38:02 +02:00
}
return string ( byt )
2013-06-06 15:12:37 +02:00
}
2018-07-18 05:54:33 +02:00
// SetEvaluationDuration updates evaluationDuration to the time in seconds it took to evaluate the rule on its last evaluation.
func ( rule * RecordingRule ) SetEvaluationDuration ( dur time . Duration ) {
2017-11-17 15:18:34 +00:00
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
2018-07-18 05:54:33 +02:00
rule . evaluationDuration = dur
2017-11-17 15:18:34 +00:00
}
2018-08-06 18:33:45 -04:00
// SetLastError sets the current error seen by the recording rule.
func ( rule * RecordingRule ) SetLastError ( err error ) {
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
rule . lastError = err
}
// LastError returns the last error seen by the recording rule.
func ( rule * RecordingRule ) LastError ( ) error {
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
return rule . lastError
}
// SetHealth sets the current health of the recording rule.
func ( rule * RecordingRule ) SetHealth ( health RuleHealth ) {
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
rule . health = health
}
// Health returns the current health of the recording rule.
func ( rule * RecordingRule ) Health ( ) RuleHealth {
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
return rule . health
}
2018-07-18 05:54:33 +02:00
// GetEvaluationDuration returns the time in seconds it took to evaluate the recording rule.
func ( rule * RecordingRule ) GetEvaluationDuration ( ) time . Duration {
2017-11-17 15:18:34 +00:00
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
2018-07-18 05:54:33 +02:00
return rule . evaluationDuration
2017-11-17 15:18:34 +00:00
}
2018-10-12 12:26:59 -04:00
// SetEvaluationTimestamp updates evaluationTimestamp to the timestamp of when the rule was last evaluated.
func ( rule * RecordingRule ) SetEvaluationTimestamp ( ts time . Time ) {
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
rule . evaluationTimestamp = ts
}
// GetEvaluationTimestamp returns the time the evaluation took place.
func ( rule * RecordingRule ) GetEvaluationTimestamp ( ) time . Time {
rule . mtx . Lock ( )
defer rule . mtx . Unlock ( )
return rule . evaluationTimestamp
}
2014-12-10 16:16:49 +01:00
// HTMLSnippet returns an HTML snippet representing this rule.
2017-11-17 15:18:34 +00:00
func ( rule * RecordingRule ) HTMLSnippet ( pathPrefix string ) template . HTML {
2013-06-13 16:10:05 +02:00
ruleExpr := rule . vector . String ( )
2017-07-08 11:38:02 +02:00
labels := make ( map [ string ] string , len ( rule . labels ) )
for _ , l := range rule . labels {
labels [ l . Name ] = template . HTMLEscapeString ( l . Value )
}
r := rulefmt . Rule {
2017-10-05 12:16:15 +02:00
Record : fmt . Sprintf ( ` <a href="%s">%s</a> ` , pathPrefix + strutil . TableLinkForExpression ( rule . name ) , rule . name ) ,
Expr : fmt . Sprintf ( ` <a href="%s">%s</a> ` , pathPrefix + strutil . TableLinkForExpression ( ruleExpr ) , template . HTMLEscapeString ( ruleExpr ) ) ,
2017-07-08 11:38:02 +02:00
Labels : labels ,
}
byt , err := yaml . Marshal ( r )
if err != nil {
2018-11-27 17:44:29 +01:00
return template . HTML ( fmt . Sprintf ( "error marshaling recording rule: %q" , template . HTMLEscapeString ( err . Error ( ) ) ) )
2017-07-08 11:38:02 +02:00
}
return template . HTML ( byt )
2013-06-13 16:10:05 +02:00
}