Merge pull request #289 from prometheus/feature/rule-strings

Implement Stringer interface for rules and all their children.
This commit is contained in:
juliusv 2013-06-07 07:00:46 -07:00
commit 25bd356aac
11 changed files with 177 additions and 129 deletions

View file

@ -13,6 +13,10 @@
package model
import (
"strings"
)
const (
// The label name indicating the metric name of a timeseries.
MetricNameLabel = LabelName("name")
@ -50,3 +54,11 @@ func (l LabelNames) Less(i, j int) bool {
func (l LabelNames) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
func (l LabelNames) String() string {
labelStrings := make([]string, 0, len(l))
for _, label := range l {
labelStrings = append(labelStrings, string(label))
}
return strings.Join(labelStrings, ", ")
}

View file

@ -19,6 +19,7 @@ import (
"fmt"
dto "github.com/prometheus/prometheus/model/generated"
"sort"
"strings"
"time"
)
@ -50,36 +51,14 @@ func (l LabelSet) Merge(other LabelSet) LabelSet {
}
func (l LabelSet) String() string {
var (
buffer bytes.Buffer
labels LabelNames
labelCount int = len(l)
)
for name := range l {
labels = append(labels, name)
labelStrings := make([]string, 0, len(l))
for label, value := range l {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value))
}
sort.Sort(labels)
sort.Strings(labelStrings)
fmt.Fprintf(&buffer, "{")
for i := 0; i < labelCount; i++ {
var (
label = labels[i]
value = l[label]
)
switch i {
case labelCount - 1:
fmt.Fprintf(&buffer, "%s=%s", label, value)
default:
fmt.Fprintf(&buffer, "%s=%s, ", label, value)
}
}
fmt.Fprintf(&buffer, "}")
return buffer.String()
return fmt.Sprintf("{%s}", strings.Join(labelStrings, ", "))
}
func (l LabelSet) ToMetric() Metric {

View file

@ -147,6 +147,10 @@ func (rule AlertingRule) ToDotGraph() string {
return graph
}
func (rule AlertingRule) String() string {
return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s\n", rule.name, rule.vector, utility.DurationToString(rule.holdDuration), rule.labels)
}
// Construct a new AlertingRule.
func NewAlertingRule(name string, vector ast.VectorNode, holdDuration time.Duration, labels model.LabelSet) *AlertingRule {
return &AlertingRule{

View file

@ -85,10 +85,13 @@ const (
// Interfaces.
// All node interfaces include the Node interface.
type Nodes []Node
type Node interface {
Type() ExprType
Children() Nodes
NodeTreeToDotGraph() string
Children() []Node
String() string
}
// All node types implement one of the following interfaces. The name of the
@ -126,7 +129,7 @@ type (
// A function of numeric return type.
ScalarFunctionCall struct {
function *Function
args []Node
args Nodes
}
// An arithmetic expression of numeric type.
@ -151,13 +154,13 @@ type (
// A function of vector return type.
VectorFunctionCall struct {
function *Function
args []Node
args Nodes
}
// A vector aggregation with vector return type.
VectorAggregation struct {
aggrType AggrType
groupBy []model.LabelName
groupBy model.LabelNames
vector VectorNode
}
@ -194,7 +197,7 @@ type (
// A function of string return type.
StringFunctionCall struct {
function *Function
args []Node
args Nodes
}
)
@ -214,16 +217,16 @@ func (node StringLiteral) Type() ExprType { return STRING }
func (node StringFunctionCall) Type() ExprType { return STRING }
// Node.Children() methods.
func (node ScalarLiteral) Children() []Node { return []Node{} }
func (node ScalarFunctionCall) Children() []Node { return node.args }
func (node ScalarArithExpr) Children() []Node { return []Node{node.lhs, node.rhs} }
func (node VectorLiteral) Children() []Node { return []Node{} }
func (node VectorFunctionCall) Children() []Node { return node.args }
func (node VectorAggregation) Children() []Node { return []Node{node.vector} }
func (node VectorArithExpr) Children() []Node { return []Node{node.lhs, node.rhs} }
func (node MatrixLiteral) Children() []Node { return []Node{} }
func (node StringLiteral) Children() []Node { return []Node{} }
func (node StringFunctionCall) Children() []Node { return node.args }
func (node ScalarLiteral) Children() Nodes { return Nodes{} }
func (node ScalarFunctionCall) Children() Nodes { return node.args }
func (node ScalarArithExpr) Children() Nodes { return Nodes{node.lhs, node.rhs} }
func (node VectorLiteral) Children() Nodes { return Nodes{} }
func (node VectorFunctionCall) Children() Nodes { return node.args }
func (node VectorAggregation) Children() Nodes { return Nodes{node.vector} }
func (node VectorArithExpr) Children() Nodes { return Nodes{node.lhs, node.rhs} }
func (node MatrixLiteral) Children() Nodes { return Nodes{} }
func (node StringLiteral) Children() Nodes { return Nodes{} }
func (node StringFunctionCall) Children() Nodes { return node.args }
func (node *ScalarLiteral) Eval(timestamp time.Time, view *viewAdapter) model.SampleValue {
return node.value
@ -622,7 +625,7 @@ func NewVectorLiteral(labels model.LabelSet) *VectorLiteral {
}
}
func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy []model.LabelName) *VectorAggregation {
func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy model.LabelNames) *VectorAggregation {
return &VectorAggregation{
aggrType: aggrType,
groupBy: groupBy,
@ -630,7 +633,7 @@ func NewVectorAggregation(aggrType AggrType, vector VectorNode, groupBy []model.
}
}
func NewFunctionCall(function *Function, args []Node) (Node, error) {
func NewFunctionCall(function *Function, args Nodes) (Node, error) {
if err := function.CheckArgTypes(args); err != nil {
return nil, err
}
@ -654,7 +657,7 @@ func NewFunctionCall(function *Function, args []Node) (Node, error) {
panic("Function with invalid return type")
}
func nodesHaveTypes(nodes []Node, exprTypes []ExprType) bool {
func nodesHaveTypes(nodes Nodes, exprTypes []ExprType) bool {
for _, node := range nodes {
correctType := false
for _, exprType := range exprTypes {
@ -670,7 +673,7 @@ func nodesHaveTypes(nodes []Node, exprTypes []ExprType) bool {
}
func NewArithExpr(opType BinOpType, lhs Node, rhs Node) (Node, error) {
if !nodesHaveTypes([]Node{lhs, rhs}, []ExprType{SCALAR, VECTOR}) {
if !nodesHaveTypes(Nodes{lhs, rhs}, []ExprType{SCALAR, VECTOR}) {
return nil, errors.New("Binary operands must be of vector or scalar type")
}
if lhs.Type() == SCALAR && rhs.Type() == VECTOR {

View file

@ -23,7 +23,8 @@ type emptyRangeNode struct{}
func (node emptyRangeNode) Type() ExprType { return MATRIX }
func (node emptyRangeNode) NodeTreeToDotGraph() string { return "" }
func (node emptyRangeNode) Children() []Node { return []Node{} }
func (node emptyRangeNode) String() string { return "" }
func (node emptyRangeNode) Children() Nodes { return Nodes{} }
func (node emptyRangeNode) Eval(timestamp time.Time, view *viewAdapter) Matrix {
return Matrix{

View file

@ -45,6 +45,8 @@ func (opType BinOpType) String() string {
NE: "!=",
GE: ">=",
LE: "<=",
AND: "AND",
OR: "OR",
}
return opTypeMap[opType]
}
@ -71,13 +73,13 @@ func (exprType ExprType) String() string {
}
func (vector Vector) String() string {
metricStrings := []string{}
metricStrings := make([]string, 0, len(vector))
for _, sample := range vector {
metricName, ok := sample.Metric[model.MetricNameLabel]
if !ok {
panic("Tried to print vector without metric name")
}
labelStrings := []string{}
labelStrings := make([]string, 0, len(sample.Metric)-1)
for label, value := range sample.Metric {
if label != model.MetricNameLabel {
// TODO escape special chars in label values here and elsewhere.
@ -95,20 +97,20 @@ func (vector Vector) String() string {
}
func (matrix Matrix) String() string {
metricStrings := []string{}
metricStrings := make([]string, 0, len(matrix))
for _, sampleSet := range matrix {
metricName, ok := sampleSet.Metric[model.MetricNameLabel]
if !ok {
panic("Tried to print matrix without metric name")
}
labelStrings := []string{}
labelStrings := make([]string, 0, len(sampleSet.Metric)-1)
for label, value := range sampleSet.Metric {
if label != model.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value))
}
}
sort.Strings(labelStrings)
valueStrings := []string{}
valueStrings := make([]string, 0, len(sampleSet.Values))
for _, value := range sampleSet.Values {
valueStrings = append(valueStrings,
fmt.Sprintf("\n%v @[%v]", value.Value, value.Timestamp))
@ -204,27 +206,6 @@ func EvalToString(node Node, timestamp time.Time, format OutputFormat, storage *
panic("Switch didn't cover all node types")
}
func (node *VectorLiteral) String() string {
metricName, ok := node.labels[model.MetricNameLabel]
if !ok {
panic("Tried to print vector without metric name")
}
labelStrings := []string{}
for label, value := range node.labels {
if label != model.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value))
}
}
sort.Strings(labelStrings)
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ","))
}
func (node *MatrixLiteral) String() string {
vectorString := (&VectorLiteral{labels: node.labels}).String()
intervalString := fmt.Sprintf("['%s']", utility.DurationToString(node.interval))
return vectorString + intervalString
}
func (node *ScalarLiteral) NodeTreeToDotGraph() string {
return fmt.Sprintf("%#p[label=\"%v\"];\n", node, node.value)
}
@ -268,7 +249,7 @@ func (node *VectorFunctionCall) NodeTreeToDotGraph() string {
}
func (node *VectorAggregation) NodeTreeToDotGraph() string {
groupByStrings := []string{}
groupByStrings := make([]string, 0, len(node.groupBy))
for _, label := range node.groupBy {
groupByStrings = append(groupByStrings, string(label))
}
@ -306,3 +287,64 @@ func (node *StringFunctionCall) NodeTreeToDotGraph() string {
graph += functionArgsToDotGraph(node, node.args)
return graph
}
func (nodes Nodes) String() string {
nodeStrings := make([]string, 0, len(nodes))
for _, node := range nodes {
nodeStrings = append(nodeStrings, node.String())
}
return strings.Join(nodeStrings, ", ")
}
func (node *ScalarLiteral) String() string {
return fmt.Sprint(node.value)
}
func (node *ScalarFunctionCall) String() string {
return fmt.Sprintf("%s(%s)", node.function.name, node.args)
}
func (node *ScalarArithExpr) String() string {
return fmt.Sprintf("(%s %s %s)", node.lhs, node.opType, node.rhs)
}
func (node *VectorLiteral) String() string {
metricName, ok := node.labels[model.MetricNameLabel]
if !ok {
panic("Tried to print vector without metric name")
}
labelStrings := make([]string, 0, len(node.labels)-1)
for label, value := range node.labels {
if label != model.MetricNameLabel {
labelStrings = append(labelStrings, fmt.Sprintf("%s='%s'", label, value))
}
}
sort.Strings(labelStrings)
return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ","))
}
func (node *VectorFunctionCall) String() string {
return fmt.Sprintf("%s(%s)", node.function.name, node.args)
}
func (node *VectorAggregation) String() string {
return fmt.Sprintf("%s(%s) BY (%s)", node.aggrType, node.vector, node.groupBy)
}
func (node *VectorArithExpr) String() string {
return fmt.Sprintf("(%s %s %s)", node.lhs, node.opType, node.rhs)
}
func (node *MatrixLiteral) String() string {
vectorString := (&VectorLiteral{labels: node.labels}).String()
intervalString := fmt.Sprintf("[%s]", utility.DurationToString(node.interval))
return vectorString + intervalString
}
func (node *StringLiteral) String() string {
return fmt.Sprintf("'%s'", node.str)
}
func (node *StringFunctionCall) String() string {
return fmt.Sprintf("%s(%s)", node.function.name, node.args)
}

View file

@ -50,7 +50,7 @@ func NewFunctionCall(name string, args []ast.Node) (ast.Node, error) {
return functionCall, nil
}
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy []model.LabelName) (*ast.VectorAggregation, error) {
func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy model.LabelNames) (*ast.VectorAggregation, error) {
if _, ok := vector.(ast.VectorNode); !ok {
return nil, fmt.Errorf("Operand of %v aggregation must be of vector type", aggrTypeStr)
}

View file

@ -14,7 +14,6 @@
%{
package rules
import "fmt"
import "github.com/prometheus/prometheus/model"
import "github.com/prometheus/prometheus/rules/ast"
%}
@ -25,7 +24,7 @@
ruleNode ast.Node
ruleNodeSlice []ast.Node
boolean bool
labelNameSlice []model.LabelName
labelNameSlice model.LabelNames
labelSet model.LabelSet
}
@ -164,13 +163,13 @@ rule_expr : '(' rule_expr ')'
;
grouping_opts :
{ $$ = []model.LabelName{} }
{ $$ = model.LabelNames{} }
| GROUP_OP '(' label_list ')'
{ $$ = $3 }
;
label_list : IDENTIFIER
{ $$ = []model.LabelName{model.LabelName($1)} }
{ $$ = model.LabelNames{model.LabelName($1)} }
| label_list ',' IDENTIFIER
{ $$ = append($$, model.LabelName($3)) }
;

View file

@ -1,12 +1,13 @@
//line parser.y:15
package rules
import __yyfmt__ "fmt"
//line parser.y:15
import "fmt"
import "github.com/prometheus/prometheus/model"
import "github.com/prometheus/prometheus/rules/ast"
//line parser.y:22
//line parser.y:21
type yySymType struct {
yys int
num model.SampleValue
@ -14,7 +15,7 @@ type yySymType struct {
ruleNode ast.Node
ruleNodeSlice []ast.Node
boolean bool
labelNameSlice []model.LabelName
labelNameSlice model.LabelNames
labelSet model.LabelSet
}
@ -60,7 +61,7 @@ const yyEofCode = 1
const yyErrCode = 2
const yyMaxDepth = 200
//line parser.y:189
//line parser.y:188
//line yacctab:1
@ -182,12 +183,13 @@ type yyLexer interface {
const yyFlag = -1000
func yyTokname(c int) string {
if c > 0 && c <= len(yyToknames) {
if yyToknames[c-1] != "" {
return yyToknames[c-1]
// 4 is TOKSTART above
if c >= 4 && c-4 < len(yyToknames) {
if yyToknames[c-4] != "" {
return yyToknames[c-4]
}
}
return fmt.Sprintf("tok-%v", c)
return __yyfmt__.Sprintf("tok-%v", c)
}
func yyStatname(s int) string {
@ -196,7 +198,7 @@ func yyStatname(s int) string {
return yyStatenames[s]
}
}
return fmt.Sprintf("state-%v", s)
return __yyfmt__.Sprintf("state-%v", s)
}
func yylex1(lex yyLexer, lval *yySymType) int {
@ -229,7 +231,7 @@ out:
c = yyTok2[1] /* unknown char */
}
if yyDebug >= 3 {
fmt.Printf("lex %U %s\n", uint(char), yyTokname(c))
__yyfmt__.Printf("lex %U %s\n", uint(char), yyTokname(c))
}
return c
}
@ -256,7 +258,7 @@ ret1:
yystack:
/* put a state and value onto the stack */
if yyDebug >= 4 {
fmt.Printf("char %v in %v\n", yyTokname(yychar), yyStatname(yystate))
__yyfmt__.Printf("char %v in %v\n", yyTokname(yychar), yyStatname(yystate))
}
yyp++
@ -325,8 +327,8 @@ yydefault:
yylex.Error("syntax error")
Nerrs++
if yyDebug >= 1 {
fmt.Printf("%s", yyStatname(yystate))
fmt.Printf("saw %s\n", yyTokname(yychar))
__yyfmt__.Printf("%s", yyStatname(yystate))
__yyfmt__.Printf("saw %s\n", yyTokname(yychar))
}
fallthrough
@ -345,7 +347,7 @@ yydefault:
/* the current p has no shift on "error", pop stack */
if yyDebug >= 2 {
fmt.Printf("error recovery pops state %d\n", yyS[yyp].yys)
__yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys)
}
yyp--
}
@ -354,7 +356,7 @@ yydefault:
case 3: /* no shift yet; clobber input char */
if yyDebug >= 2 {
fmt.Printf("error recovery discards %s\n", yyTokname(yychar))
__yyfmt__.Printf("error recovery discards %s\n", yyTokname(yychar))
}
if yychar == yyEofCode {
goto ret1
@ -366,7 +368,7 @@ yydefault:
/* reduction by production yyn */
if yyDebug >= 2 {
fmt.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate))
__yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate))
}
yynt := yyn
@ -393,133 +395,133 @@ yydefault:
switch yynt {
case 5:
//line parser.y:67
//line parser.y:66
{ yylex.(*RulesLexer).parsedExpr = yyS[yypt-0].ruleNode }
case 6:
//line parser.y:71
//line parser.y:70
{
rule, err := CreateRecordingRule(yyS[yypt-3].str, yyS[yypt-2].labelSet, yyS[yypt-0].ruleNode, yyS[yypt-4].boolean)
if err != nil { yylex.Error(err.Error()); return 1 }
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
}
case 7:
//line parser.y:77
//line parser.y:76
{
rule, err := CreateAlertingRule(yyS[yypt-5].str, yyS[yypt-3].ruleNode, yyS[yypt-2].str, yyS[yypt-0].labelSet)
if err != nil { yylex.Error(err.Error()); return 1 }
yylex.(*RulesLexer).parsedRules = append(yylex.(*RulesLexer).parsedRules, rule)
}
case 8:
//line parser.y:85
//line parser.y:84
{ yyVAL.str = "0s" }
case 9:
//line parser.y:87
//line parser.y:86
{ yyVAL.str = yyS[yypt-0].str }
case 10:
//line parser.y:91
//line parser.y:90
{ yyVAL.boolean = false }
case 11:
//line parser.y:93
//line parser.y:92
{ yyVAL.boolean = true }
case 12:
//line parser.y:97
//line parser.y:96
{ yyVAL.labelSet = model.LabelSet{} }
case 13:
//line parser.y:99
//line parser.y:98
{ yyVAL.labelSet = yyS[yypt-1].labelSet }
case 14:
//line parser.y:101
//line parser.y:100
{ yyVAL.labelSet = model.LabelSet{} }
case 15:
//line parser.y:104
//line parser.y:103
{ yyVAL.labelSet = yyS[yypt-0].labelSet }
case 16:
//line parser.y:106
//line parser.y:105
{ for k, v := range yyS[yypt-0].labelSet { yyVAL.labelSet[k] = v } }
case 17:
//line parser.y:110
//line parser.y:109
{ yyVAL.labelSet = model.LabelSet{ model.LabelName(yyS[yypt-2].str): model.LabelValue(yyS[yypt-0].str) } }
case 18:
//line parser.y:115
//line parser.y:114
{ yyVAL.ruleNode = yyS[yypt-1].ruleNode }
case 19:
//line parser.y:117
//line parser.y:116
{ yyS[yypt-0].labelSet[model.MetricNameLabel] = model.LabelValue(yyS[yypt-1].str); yyVAL.ruleNode = ast.NewVectorLiteral(yyS[yypt-0].labelSet) }
case 20:
//line parser.y:119
//line parser.y:118
{
var err error
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-3].str, yyS[yypt-1].ruleNodeSlice)
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 21:
//line parser.y:125
//line parser.y:124
{
var err error
yyVAL.ruleNode, err = NewFunctionCall(yyS[yypt-2].str, []ast.Node{})
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 22:
//line parser.y:131
//line parser.y:130
{
var err error
yyVAL.ruleNode, err = NewMatrix(yyS[yypt-3].ruleNode, yyS[yypt-1].str)
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 23:
//line parser.y:137
//line parser.y:136
{
var err error
yyVAL.ruleNode, err = NewVectorAggregation(yyS[yypt-4].str, yyS[yypt-2].ruleNode, yyS[yypt-0].labelNameSlice)
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 24:
//line parser.y:145
//line parser.y:144
{
var err error
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 25:
//line parser.y:151
//line parser.y:150
{
var err error
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 26:
//line parser.y:157
//line parser.y:156
{
var err error
yyVAL.ruleNode, err = NewArithExpr(yyS[yypt-1].str, yyS[yypt-2].ruleNode, yyS[yypt-0].ruleNode)
if err != nil { yylex.Error(err.Error()); return 1 }
}
case 27:
//line parser.y:163
//line parser.y:162
{ yyVAL.ruleNode = ast.NewScalarLiteral(yyS[yypt-0].num)}
case 28:
//line parser.y:167
{ yyVAL.labelNameSlice = []model.LabelName{} }
//line parser.y:166
{ yyVAL.labelNameSlice = model.LabelNames{} }
case 29:
//line parser.y:169
//line parser.y:168
{ yyVAL.labelNameSlice = yyS[yypt-1].labelNameSlice }
case 30:
//line parser.y:173
{ yyVAL.labelNameSlice = []model.LabelName{model.LabelName(yyS[yypt-0].str)} }
//line parser.y:172
{ yyVAL.labelNameSlice = model.LabelNames{model.LabelName(yyS[yypt-0].str)} }
case 31:
//line parser.y:175
//line parser.y:174
{ yyVAL.labelNameSlice = append(yyVAL.labelNameSlice, model.LabelName(yyS[yypt-0].str)) }
case 32:
//line parser.y:179
//line parser.y:178
{ yyVAL.ruleNodeSlice = []ast.Node{yyS[yypt-0].ruleNode} }
case 33:
//line parser.y:181
//line parser.y:180
{ yyVAL.ruleNodeSlice = append(yyVAL.ruleNodeSlice, yyS[yypt-0].ruleNode) }
case 34:
//line parser.y:185
//line parser.y:184
{ yyVAL.ruleNode = yyS[yypt-0].ruleNode }
case 35:
//line parser.y:187
//line parser.y:186
{ yyVAL.ruleNode = ast.NewStringLiteral(yyS[yypt-0].str) }
}
goto yystack /* stack new state and value */

View file

@ -67,6 +67,10 @@ func (rule RecordingRule) ToDotGraph() string {
return graph
}
func (rule RecordingRule) String() string {
return fmt.Sprintf("%s%s = %s\n", rule.name, rule.labels, rule.vector)
}
// Construct a new RecordingRule.
func NewRecordingRule(name string, labels model.LabelSet, vector ast.VectorNode, permanent bool) *RecordingRule {
return &RecordingRule{

View file

@ -31,4 +31,6 @@ type Rule interface {
Eval(timestamp time.Time, storage *metric.TieredStorage) (ast.Vector, error)
// ToDotGraph returns a Graphviz dot graph of the rule.
ToDotGraph() string
// String returns a human-readable string representation of the rule.
String() string
}