From 74aed55e558408d494eca517aebad05b60b25684 Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Thu, 16 Apr 2015 23:19:04 +0100 Subject: [PATCH 01/13] Add square root function --- rules/ast/functions.go | 17 +++++++++++++++++ rules/rules_test.go | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/rules/ast/functions.go b/rules/ast/functions.go index ea95a1966a..5c7ae096aa 100644 --- a/rules/ast/functions.go +++ b/rules/ast/functions.go @@ -500,6 +500,17 @@ func log10Impl(timestamp clientmodel.Timestamp, args []Node) interface{} { return vector } +// === sqrt(vector VectorNode) Vector === +func sqrtImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { + n := args[0].(VectorNode) + vector := n.Eval(timestamp) + for _, el := range vector { + el.Metric.Delete(clientmodel.MetricNameLabel) + el.Value = clientmodel.SampleValue(math.Sqrt(float64(el.Value))) + } + return vector +} + // === deriv(node MatrixNode) Vector === func derivImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { matrixNode := args[0].(MatrixNode) @@ -721,6 +732,12 @@ var functions = map[string]*Function{ returnType: VectorType, callFn: sortDescImpl, }, + "sqrt": { + name: "sqrt", + argTypes: []ExprType{VectorType}, + returnType: VectorType, + callFn: sqrtImpl, + }, "sum_over_time": { name: "sum_over_time", argTypes: []ExprType{MatrixType}, diff --git a/rules/rules_test.go b/rules/rules_test.go index 5966c21ec0..1ff76a070a 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -1197,6 +1197,13 @@ func TestExpressions(t *testing.T) { `{group="canary", instance="0", job="api-server"} => NaN @[%v]`, }, }, + { + expr: `sqrt(vector_matching_a)`, + output: []string{ + `{l="x"} => 3.1622776601683795 @[%v]`, + `{l="y"} => 4.47213595499958 @[%v]`, + }, + }, { expr: `exp(vector_matching_a)`, output: []string{ From c3a2b63fe926d5bbf054a2900af7d14c798405ed Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Fri, 17 Apr 2015 00:12:04 +0100 Subject: [PATCH 02/13] Add stddev and stdvar aggregation functions. This adds the population standard deviation and variance as aggregation functions, useful for spotting how many standard deviations some samples are from the mean. --- rules/ast/ast.go | 26 +- rules/ast/printer.go | 12 +- rules/helpers.go | 12 +- rules/lexer.l | 4 +- rules/lexer.l.go | 1216 +++++++++++++++++++++----------------- rules/manager/manager.go | 4 +- rules/rules_test.go | 30 +- templates/templates.go | 2 +- web/api/api.go | 12 +- web/consoles.go | 2 +- web/status.go | 2 +- web/web.go | 32 +- 12 files changed, 781 insertions(+), 573 deletions(-) diff --git a/rules/ast/ast.go b/rules/ast/ast.go index a279a4f9d8..eff53ede50 100644 --- a/rules/ast/ast.go +++ b/rules/ast/ast.go @@ -66,9 +66,10 @@ type Vector []*Sample type Matrix []SampleStream type groupedAggregation struct { - labels clientmodel.COWMetric - value clientmodel.SampleValue - groupCount int + labels clientmodel.COWMetric + value clientmodel.SampleValue + valuesSquaredSum clientmodel.SampleValue + groupCount int } // ---------------------------------------------------------------------------- @@ -128,6 +129,8 @@ const ( Min Max Count + Stdvar + Stddev ) // ---------------------------------------------------------------------------- @@ -468,6 +471,12 @@ func (node *VectorAggregation) groupedAggregationsToVector(aggregations map[uint aggregation.value = aggregation.value / clientmodel.SampleValue(aggregation.groupCount) case Count: aggregation.value = clientmodel.SampleValue(aggregation.groupCount) + case Stdvar: + avg := float64(aggregation.value) / float64(aggregation.groupCount) + aggregation.value = clientmodel.SampleValue(float64(aggregation.valuesSquaredSum)/float64(aggregation.groupCount) - avg*avg) + case Stddev: + avg := float64(aggregation.value) / float64(aggregation.groupCount) + aggregation.value = clientmodel.SampleValue(math.Sqrt(float64(aggregation.valuesSquaredSum)/float64(aggregation.groupCount) - avg*avg)) default: // For other aggregations, we already have the right value. } @@ -509,6 +518,10 @@ func (node *VectorAggregation) Eval(timestamp clientmodel.Timestamp) Vector { } case Count: groupedResult.groupCount++ + case Stdvar, Stddev: + groupedResult.value += sample.Value + groupedResult.valuesSquaredSum += sample.Value * sample.Value + groupedResult.groupCount++ default: panic("Unknown aggregation type") } @@ -529,9 +542,10 @@ func (node *VectorAggregation) Eval(timestamp clientmodel.Timestamp) Vector { } } result[groupingKey] = &groupedAggregation{ - labels: m, - value: sample.Value, - groupCount: 1, + labels: m, + value: sample.Value, + valuesSquaredSum: sample.Value * sample.Value, + groupCount: 1, } } } diff --git a/rules/ast/printer.go b/rules/ast/printer.go index 7c9ebcade3..182f8544a6 100644 --- a/rules/ast/printer.go +++ b/rules/ast/printer.go @@ -60,11 +60,13 @@ func (opType BinOpType) String() string { func (aggrType AggrType) String() string { aggrTypeMap := map[AggrType]string{ - Sum: "SUM", - Avg: "AVG", - Min: "MIN", - Max: "MAX", - Count: "COUNT", + Sum: "SUM", + Avg: "AVG", + Min: "MIN", + Max: "MAX", + Count: "COUNT", + Stdvar: "STDVAR", + Stddev: "STDDEV", } return aggrTypeMap[aggrType] } diff --git a/rules/helpers.go b/rules/helpers.go index 9f41622158..0a53fb1266 100644 --- a/rules/helpers.go +++ b/rules/helpers.go @@ -78,11 +78,13 @@ func NewVectorAggregation(aggrTypeStr string, vector ast.Node, groupBy clientmod return nil, fmt.Errorf("operand of %v aggregation must be of vector type", aggrTypeStr) } var aggrTypes = map[string]ast.AggrType{ - "SUM": ast.Sum, - "MAX": ast.Max, - "MIN": ast.Min, - "AVG": ast.Avg, - "COUNT": ast.Count, + "SUM": ast.Sum, + "MAX": ast.Max, + "MIN": ast.Min, + "AVG": ast.Avg, + "COUNT": ast.Count, + "STDVAR": ast.Stdvar, + "STDDEV": ast.Stddev, } aggrType, ok := aggrTypes[aggrTypeStr] if !ok { diff --git a/rules/lexer.l b/rules/lexer.l index d4c9d19af0..8bb20a2c8f 100644 --- a/rules/lexer.l +++ b/rules/lexer.l @@ -89,8 +89,8 @@ GROUP_LEFT|GROUP_RIGHT lval.str = lexer.token(); return MATCH_MOD group_left|group_right lval.str = strings.ToUpper(lexer.token()); return MATCH_MOD KEEPING_EXTRA|keeping_extra return KEEPING_EXTRA OFFSET|offset return OFFSET -AVG|SUM|MAX|MIN|COUNT lval.str = lexer.token(); return AGGR_OP -avg|sum|max|min|count lval.str = strings.ToUpper(lexer.token()); return AGGR_OP +AVG|SUM|MAX|MIN|COUNT|STDVAR|STDDEV lval.str = lexer.token(); return AGGR_OP +avg|sum|max|min|count|stdvar|stddev lval.str = strings.ToUpper(lexer.token()); return AGGR_OP \<|>|AND|OR|and|or lval.str = strings.ToUpper(lexer.token()); return CMP_OP ==|!=|>=|<=|=~|!~ lval.str = lexer.token(); return CMP_OP [+\-] lval.str = lexer.token(); return ADDITIVE_OP diff --git a/rules/lexer.l.go b/rules/lexer.l.go index 892d1befdb..ade39fa7fb 100644 --- a/rules/lexer.l.go +++ b/rules/lexer.l.go @@ -56,7 +56,7 @@ yystate0: case 0: // start condition: INITIAL goto yystart1 case 1: // start condition: S_COMMENTS - goto yystart197 + goto yystart209 } goto yystate0 // silence unused label error @@ -116,37 +116,37 @@ yystart1: case c == 'S': goto yystate111 case c == 'W': - goto yystate118 + goto yystate124 case c == '\'': goto yystate9 case c == '\t' || c == '\n' || c == '\r' || c == ' ': goto yystate2 case c == 'a': - goto yystate122 + goto yystate128 case c == 'b': - goto yystate129 + goto yystate135 case c == 'c': - goto yystate130 + goto yystate136 case c == 'd': - goto yystate134 + goto yystate140 case c == 'f': - goto yystate144 + goto yystate150 case c == 'g': - goto yystate146 + goto yystate152 case c == 'i': - goto yystate159 + goto yystate165 case c == 'k': - goto yystate160 + goto yystate166 case c == 'm': - goto yystate172 + goto yystate178 case c == 'o': - goto yystate175 + goto yystate181 case c == 'p': - goto yystate180 + goto yystate186 case c == 's': - goto yystate188 - case c == 'w': goto yystate194 + case c == 'w': + goto yystate206 case c >= '0' && c <= '9': goto yystate25 } @@ -1426,9 +1426,11 @@ yystate111: goto yyrule26 case c == ':': goto yystate27 - case c == 'U': + case c == 'T': goto yystate112 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'T' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c == 'U': + goto yystate118 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'V' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1439,9 +1441,9 @@ yystate112: goto yyrule26 case c == ':': goto yystate27 - case c == 'M': + case c == 'D': goto yystate113 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1449,12 +1451,14 @@ yystate113: c = lexer.getChar() switch { default: - goto yyrule18 + goto yyrule26 case c == ':': goto yystate27 - case c == 'M': + case c == 'D': goto yystate114 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c == 'V': + goto yystate116 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'C' || c >= 'E' && c <= 'U' || c >= 'W' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1465,9 +1469,9 @@ yystate114: goto yyrule26 case c == ':': goto yystate27 - case c == 'A': + case c == 'E': goto yystate115 - case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'D' || c >= 'F' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1478,9 +1482,9 @@ yystate115: goto yyrule26 case c == ':': goto yystate27 - case c == 'R': - goto yystate116 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c == 'V': + goto yystate39 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'U' || c >= 'W' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1491,9 +1495,9 @@ yystate116: goto yyrule26 case c == ':': goto yystate27 - case c == 'Y': + case c == 'A': goto yystate117 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'X' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1501,10 +1505,12 @@ yystate117: c = lexer.getChar() switch { default: - goto yyrule9 + goto yyrule26 case c == ':': goto yystate27 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c == 'R': + goto yystate39 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1515,9 +1521,9 @@ yystate118: goto yyrule26 case c == ':': goto yystate27 - case c == 'I': + case c == 'M': goto yystate119 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1525,12 +1531,12 @@ yystate119: c = lexer.getChar() switch { default: - goto yyrule26 + goto yyrule18 case c == ':': goto yystate27 - case c == 'T': + case c == 'M': goto yystate120 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'L' || c >= 'N' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1541,9 +1547,9 @@ yystate120: goto yyrule26 case c == ':': goto yystate27 - case c == 'H': + case c == 'A': goto yystate121 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'G' || c >= 'I' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'B' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1551,10 +1557,12 @@ yystate121: c = lexer.getChar() switch { default: - goto yyrule8 + goto yyrule26 case c == ':': goto yystate27 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c == 'R': + goto yystate122 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Q' || c >= 'S' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1565,13 +1573,9 @@ yystate122: goto yyrule26 case c == ':': goto yystate27 - case c == 'l': + case c == 'Y': goto yystate123 - case c == 'n': - goto yystate126 - case c == 'v': - goto yystate127 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c == 'm' || c >= 'o' && c <= 'u' || c >= 'w' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'X' || c == 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1579,12 +1583,10 @@ yystate123: c = lexer.getChar() switch { default: - goto yyrule26 + goto yyrule9 case c == ':': goto yystate27 - case c == 'e': - goto yystate124 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } @@ -1595,13 +1597,93 @@ yystate124: goto yyrule26 case c == ':': goto yystate27 - case c == 'r': + case c == 'I': goto yystate125 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'H' || c >= 'J' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': goto yystate31 } yystate125: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'T': + goto yystate126 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'S' || c >= 'U' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate31 + } + +yystate126: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'H': + goto yystate127 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'G' || c >= 'I' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate31 + } + +yystate127: + c = lexer.getChar() + switch { + default: + goto yyrule8 + case c == ':': + goto yystate27 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate31 + } + +yystate128: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'l': + goto yystate129 + case c == 'n': + goto yystate132 + case c == 'v': + goto yystate133 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c == 'm' || c >= 'o' && c <= 'u' || c >= 'w' && c <= 'z': + goto yystate31 + } + +yystate129: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'e': + goto yystate130 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate31 + } + +yystate130: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'r': + goto yystate131 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate31 + } + +yystate131: c = lexer.getChar() switch { default: @@ -1614,7 +1696,7 @@ yystate125: goto yystate31 } -yystate126: +yystate132: c = lexer.getChar() switch { default: @@ -1627,7 +1709,7 @@ yystate126: goto yystate31 } -yystate127: +yystate133: c = lexer.getChar() switch { default: @@ -1635,12 +1717,12 @@ yystate127: case c == ':': goto yystate27 case c == 'g': - goto yystate128 + goto yystate134 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': goto yystate31 } -yystate128: +yystate134: c = lexer.getChar() switch { default: @@ -1651,7 +1733,7 @@ yystate128: goto yystate31 } -yystate129: +yystate135: c = lexer.getChar() switch { default: @@ -1664,84 +1746,6 @@ yystate129: goto yystate31 } -yystate130: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'o': - goto yystate131 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': - goto yystate31 - } - -yystate131: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'u': - goto yystate132 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': - goto yystate31 - } - -yystate132: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'n': - goto yystate133 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': - goto yystate31 - } - -yystate133: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 't': - goto yystate128 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': - goto yystate31 - } - -yystate134: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'e': - goto yystate135 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': - goto yystate31 - } - -yystate135: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 's': - goto yystate136 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': - goto yystate31 - } - yystate136: c = lexer.getChar() switch { @@ -1749,9 +1753,9 @@ yystate136: goto yyrule26 case c == ':': goto yystate27 - case c == 'c': + case c == 'o': goto yystate137 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': goto yystate31 } @@ -1762,9 +1766,9 @@ yystate137: goto yyrule26 case c == ':': goto yystate27 - case c == 'r': + case c == 'u': goto yystate138 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': goto yystate31 } @@ -1775,9 +1779,9 @@ yystate138: goto yyrule26 case c == ':': goto yystate27 - case c == 'i': + case c == 'n': goto yystate139 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': goto yystate31 } @@ -1788,9 +1792,9 @@ yystate139: goto yyrule26 case c == ':': goto yystate27 - case c == 'p': - goto yystate140 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': + case c == 't': + goto yystate134 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': goto yystate31 } @@ -1801,9 +1805,9 @@ yystate140: goto yyrule26 case c == ':': goto yystate27 - case c == 't': + case c == 'e': goto yystate141 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate31 } @@ -1814,9 +1818,9 @@ yystate141: goto yyrule26 case c == ':': goto yystate27 - case c == 'i': + case c == 's': goto yystate142 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': goto yystate31 } @@ -1827,13 +1831,91 @@ yystate142: goto yyrule26 case c == ':': goto yystate27 - case c == 'o': + case c == 'c': goto yystate143 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c == 'a' || c == 'b' || c >= 'd' && c <= 'z': goto yystate31 } yystate143: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'r': + goto yystate144 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate31 + } + +yystate144: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'i': + goto yystate145 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate31 + } + +yystate145: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'p': + goto yystate146 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': + goto yystate31 + } + +yystate146: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 't': + goto yystate147 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate31 + } + +yystate147: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'i': + goto yystate148 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate31 + } + +yystate148: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'o': + goto yystate149 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': + goto yystate31 + } + +yystate149: c = lexer.getChar() switch { default: @@ -1846,7 +1928,7 @@ yystate143: goto yystate31 } -yystate144: +yystate150: c = lexer.getChar() switch { default: @@ -1854,12 +1936,12 @@ yystate144: case c == ':': goto yystate27 case c == 'o': - goto yystate145 + goto yystate151 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': goto yystate31 } -yystate145: +yystate151: c = lexer.getChar() switch { default: @@ -1872,86 +1954,6 @@ yystate145: goto yystate31 } -yystate146: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'r': - goto yystate147 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': - goto yystate31 - } - -yystate147: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'o': - goto yystate148 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': - goto yystate31 - } - -yystate148: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'u': - goto yystate149 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': - goto yystate31 - } - -yystate149: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'p': - goto yystate150 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': - goto yystate31 - } - -yystate150: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == '_': - goto yystate151 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z': - goto yystate31 - } - -yystate151: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'l': - goto yystate152 - case c == 'r': - goto yystate156 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'q' || c >= 's' && c <= 'z': - goto yystate31 - } - yystate152: c = lexer.getChar() switch { @@ -1959,9 +1961,9 @@ yystate152: goto yyrule26 case c == ':': goto yystate27 - case c == 'e': + case c == 'r': goto yystate153 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': goto yystate31 } @@ -1972,9 +1974,9 @@ yystate153: goto yyrule26 case c == ':': goto yystate27 - case c == 'f': + case c == 'o': goto yystate154 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'n' || c >= 'p' && c <= 'z': goto yystate31 } @@ -1985,9 +1987,9 @@ yystate154: goto yyrule26 case c == ':': goto yystate27 - case c == 't': + case c == 'u': goto yystate155 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': goto yystate31 } @@ -1995,10 +1997,12 @@ yystate155: c = lexer.getChar() switch { default: - goto yyrule15 + goto yyrule26 case c == ':': goto yystate27 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + case c == 'p': + goto yystate156 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': goto yystate31 } @@ -2009,9 +2013,9 @@ yystate156: goto yyrule26 case c == ':': goto yystate27 - case c == 'i': + case c == '_': goto yystate157 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z': goto yystate31 } @@ -2022,9 +2026,11 @@ yystate157: goto yyrule26 case c == ':': goto yystate27 - case c == 'g': + case c == 'l': goto yystate158 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': + case c == 'r': + goto yystate162 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'k' || c >= 'm' && c <= 'q' || c >= 's' && c <= 'z': goto yystate31 } @@ -2035,13 +2041,89 @@ yystate158: goto yyrule26 case c == ':': goto yystate27 - case c == 'h': - goto yystate154 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': + case c == 'e': + goto yystate159 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate31 } yystate159: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'f': + goto yystate160 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': + goto yystate31 + } + +yystate160: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 't': + goto yystate161 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate31 + } + +yystate161: + c = lexer.getChar() + switch { + default: + goto yyrule15 + case c == ':': + goto yystate27 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'z': + goto yystate31 + } + +yystate162: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'i': + goto yystate163 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate31 + } + +yystate163: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'g': + goto yystate164 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': + goto yystate31 + } + +yystate164: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'h': + goto yystate160 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': + goto yystate31 + } + +yystate165: c = lexer.getChar() switch { default: @@ -2056,84 +2138,6 @@ yystate159: goto yystate31 } -yystate160: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'e': - goto yystate161 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': - goto yystate31 - } - -yystate161: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'e': - goto yystate162 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': - goto yystate31 - } - -yystate162: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'p': - goto yystate163 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': - goto yystate31 - } - -yystate163: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'i': - goto yystate164 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': - goto yystate31 - } - -yystate164: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'n': - goto yystate165 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': - goto yystate31 - } - -yystate165: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'g': - goto yystate166 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': - goto yystate31 - } - yystate166: c = lexer.getChar() switch { @@ -2141,9 +2145,9 @@ yystate166: goto yyrule26 case c == ':': goto yystate27 - case c == '_': + case c == 'e': goto yystate167 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate31 } @@ -2167,9 +2171,9 @@ yystate168: goto yyrule26 case c == ':': goto yystate27 - case c == 'x': + case c == 'p': goto yystate169 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'o' || c >= 'q' && c <= 'z': goto yystate31 } @@ -2180,9 +2184,9 @@ yystate169: goto yyrule26 case c == ':': goto yystate27 - case c == 't': + case c == 'i': goto yystate170 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': goto yystate31 } @@ -2193,13 +2197,91 @@ yystate170: goto yyrule26 case c == ':': goto yystate27 - case c == 'r': + case c == 'n': goto yystate171 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': goto yystate31 } yystate171: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'g': + goto yystate172 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'f' || c >= 'h' && c <= 'z': + goto yystate31 + } + +yystate172: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == '_': + goto yystate173 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z': + goto yystate31 + } + +yystate173: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'e': + goto yystate174 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate31 + } + +yystate174: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'x': + goto yystate175 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': + goto yystate31 + } + +yystate175: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 't': + goto yystate176 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate31 + } + +yystate176: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'r': + goto yystate177 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate31 + } + +yystate177: c = lexer.getChar() switch { default: @@ -2212,7 +2294,7 @@ yystate171: goto yystate31 } -yystate172: +yystate178: c = lexer.getChar() switch { default: @@ -2220,14 +2302,14 @@ yystate172: case c == ':': goto yystate27 case c == 'a': - goto yystate173 + goto yystate179 case c == 'i': - goto yystate174 + goto yystate180 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'h' || c >= 'j' && c <= 'z': goto yystate31 } -yystate173: +yystate179: c = lexer.getChar() switch { default: @@ -2235,12 +2317,12 @@ yystate173: case c == ':': goto yystate27 case c == 'x': - goto yystate128 + goto yystate134 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'w' || c == 'y' || c == 'z': goto yystate31 } -yystate174: +yystate180: c = lexer.getChar() switch { default: @@ -2248,12 +2330,12 @@ yystate174: case c == ':': goto yystate27 case c == 'n': - goto yystate128 + goto yystate134 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': goto yystate31 } -yystate175: +yystate181: c = lexer.getChar() switch { default: @@ -2261,7 +2343,7 @@ yystate175: case c == ':': goto yystate27 case c == 'f': - goto yystate176 + goto yystate182 case c == 'n': goto yystate101 case c == 'r': @@ -2270,7 +2352,7 @@ yystate175: goto yystate31 } -yystate176: +yystate182: c = lexer.getChar() switch { default: @@ -2278,12 +2360,12 @@ yystate176: case c == ':': goto yystate27 case c == 'f': - goto yystate177 + goto yystate183 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'e' || c >= 'g' && c <= 'z': goto yystate31 } -yystate177: +yystate183: c = lexer.getChar() switch { default: @@ -2291,12 +2373,12 @@ yystate177: case c == ':': goto yystate27 case c == 's': - goto yystate178 + goto yystate184 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'r' || c >= 't' && c <= 'z': goto yystate31 } -yystate178: +yystate184: c = lexer.getChar() switch { default: @@ -2304,12 +2386,12 @@ yystate178: case c == ':': goto yystate27 case c == 'e': - goto yystate179 + goto yystate185 case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate31 } -yystate179: +yystate185: c = lexer.getChar() switch { default: @@ -2322,84 +2404,6 @@ yystate179: goto yystate31 } -yystate180: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'e': - goto yystate181 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': - goto yystate31 - } - -yystate181: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'r': - goto yystate182 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': - goto yystate31 - } - -yystate182: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'm': - goto yystate183 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': - goto yystate31 - } - -yystate183: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'a': - goto yystate184 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': - goto yystate31 - } - -yystate184: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'n': - goto yystate185 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': - goto yystate31 - } - -yystate185: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'e': - goto yystate186 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': - goto yystate31 - } - yystate186: c = lexer.getChar() switch { @@ -2407,13 +2411,91 @@ yystate186: goto yyrule26 case c == ':': goto yystate27 - case c == 'n': + case c == 'e': goto yystate187 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': goto yystate31 } yystate187: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'r': + goto yystate188 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate31 + } + +yystate188: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'm': + goto yystate189 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': + goto yystate31 + } + +yystate189: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'a': + goto yystate190 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate31 + } + +yystate190: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'n': + goto yystate191 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate31 + } + +yystate191: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'e': + goto yystate192 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate31 + } + +yystate192: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'n': + goto yystate193 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'm' || c >= 'o' && c <= 'z': + goto yystate31 + } + +yystate193: c = lexer.getChar() switch { default: @@ -2426,84 +2508,6 @@ yystate187: goto yystate31 } -yystate188: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'u': - goto yystate189 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 't' || c >= 'v' && c <= 'z': - goto yystate31 - } - -yystate189: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'm': - goto yystate190 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': - goto yystate31 - } - -yystate190: - c = lexer.getChar() - switch { - default: - goto yyrule19 - case c == ':': - goto yystate27 - case c == 'm': - goto yystate191 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': - goto yystate31 - } - -yystate191: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'a': - goto yystate192 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': - goto yystate31 - } - -yystate192: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'r': - goto yystate193 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': - goto yystate31 - } - -yystate193: - c = lexer.getChar() - switch { - default: - goto yyrule26 - case c == ':': - goto yystate27 - case c == 'y': - goto yystate117 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'x' || c == 'z': - goto yystate31 - } - yystate194: c = lexer.getChar() switch { @@ -2511,9 +2515,11 @@ yystate194: goto yyrule26 case c == ':': goto yystate27 - case c == 'i': + case c == 't': goto yystate195 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + case c == 'u': + goto yystate201 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'v' && c <= 'z': goto yystate31 } @@ -2524,9 +2530,9 @@ yystate195: goto yyrule26 case c == ':': goto yystate27 - case c == 't': + case c == 'd': goto yystate196 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'z': goto yystate31 } @@ -2537,39 +2543,197 @@ yystate196: goto yyrule26 case c == ':': goto yystate27 - case c == 'h': - goto yystate121 - case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': + case c == 'd': + goto yystate197 + case c == 'v': + goto yystate199 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'c' || c >= 'e' && c <= 'u' || c >= 'w' && c <= 'z': goto yystate31 } - goto yystate197 // silence unused label error yystate197: c = lexer.getChar() -yystart197: switch { default: - goto yyabort - case c == '*': - goto yystate199 - case c >= '\x01' && c <= ')' || c >= '+' && c <= 'ÿ': + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'e': goto yystate198 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'd' || c >= 'f' && c <= 'z': + goto yystate31 } yystate198: c = lexer.getChar() - goto yyrule3 + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'v': + goto yystate134 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'u' || c >= 'w' && c <= 'z': + goto yystate31 + } yystate199: c = lexer.getChar() switch { default: - goto yyrule3 - case c == '/': + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'a': goto yystate200 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate31 } yystate200: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'r': + goto yystate134 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate31 + } + +yystate201: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'm': + goto yystate202 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': + goto yystate31 + } + +yystate202: + c = lexer.getChar() + switch { + default: + goto yyrule19 + case c == ':': + goto yystate27 + case c == 'm': + goto yystate203 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'l' || c >= 'n' && c <= 'z': + goto yystate31 + } + +yystate203: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'a': + goto yystate204 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'b' && c <= 'z': + goto yystate31 + } + +yystate204: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'r': + goto yystate205 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'q' || c >= 's' && c <= 'z': + goto yystate31 + } + +yystate205: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'y': + goto yystate123 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'x' || c == 'z': + goto yystate31 + } + +yystate206: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'i': + goto yystate207 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'h' || c >= 'j' && c <= 'z': + goto yystate31 + } + +yystate207: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 't': + goto yystate208 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 's' || c >= 'u' && c <= 'z': + goto yystate31 + } + +yystate208: + c = lexer.getChar() + switch { + default: + goto yyrule26 + case c == ':': + goto yystate27 + case c == 'h': + goto yystate127 + case c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c == '_' || c >= 'a' && c <= 'g' || c >= 'i' && c <= 'z': + goto yystate31 + } + + goto yystate209 // silence unused label error +yystate209: + c = lexer.getChar() +yystart209: + switch { + default: + goto yyabort + case c == '*': + goto yystate211 + case c >= '\x01' && c <= ')' || c >= '+' && c <= 'ÿ': + goto yystate210 + } + +yystate210: + c = lexer.getChar() + goto yyrule3 + +yystate211: + c = lexer.getChar() + switch { + default: + goto yyrule3 + case c == '/': + goto yystate212 + } + +yystate212: c = lexer.getChar() goto yyrule2 @@ -2649,13 +2813,13 @@ yyrule17: // OFFSET|offset { return OFFSET } -yyrule18: // AVG|SUM|MAX|MIN|COUNT +yyrule18: // AVG|SUM|MAX|MIN|COUNT|STDVAR|STDDEV { lval.str = lexer.token() return AGGR_OP goto yystate0 } -yyrule19: // avg|sum|max|min|count +yyrule19: // avg|sum|max|min|count|stdvar|stddev { lval.str = strings.ToUpper(lexer.token()) return AGGR_OP diff --git a/rules/manager/manager.go b/rules/manager/manager.go index 0b1a386a5c..6865e7df5e 100644 --- a/rules/manager/manager.go +++ b/rules/manager/manager.go @@ -99,7 +99,7 @@ type ruleManager struct { notificationHandler *notification.NotificationHandler prometheusURL string - pathPrefix string + pathPrefix string } // RuleManagerOptions bundles options for the RuleManager. @@ -111,7 +111,7 @@ type RuleManagerOptions struct { SampleAppender storage.SampleAppender PrometheusURL string - PathPrefix string + PathPrefix string } // NewRuleManager returns an implementation of RuleManager, ready to be started diff --git a/rules/rules_test.go b/rules/rules_test.go index 1ff76a070a..e19069d59d 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -1197,13 +1197,13 @@ func TestExpressions(t *testing.T) { `{group="canary", instance="0", job="api-server"} => NaN @[%v]`, }, }, - { + { expr: `sqrt(vector_matching_a)`, output: []string{ `{l="x"} => 3.1622776601683795 @[%v]`, `{l="y"} => 4.47213595499958 @[%v]`, }, - }, + }, { expr: `exp(vector_matching_a)`, output: []string{ @@ -1295,6 +1295,32 @@ func TestExpressions(t *testing.T) { `{l="y"} => -Inf @[%v]`, }, }, + { + expr: `stddev(http_requests)`, + output: []string{ + `{} => 229.12878474779 @[%v]`, + }, + }, + { + expr: `stddev by (instance)(http_requests)`, + output: []string{ + `{instance="0"} => 223.60679774998 @[%v]`, + `{instance="1"} => 223.60679774998 @[%v]`, + }, + }, + { + expr: `stdvar(http_requests)`, + output: []string{ + `{} => 52500 @[%v]`, + }, + }, + { + expr: `stdvar by (instance)(http_requests)`, + output: []string{ + `{instance="0"} => 50000 @[%v]`, + `{instance="1"} => 50000 @[%v]`, + }, + }, } storage, closer := newTestStorage(t) diff --git a/templates/templates.go b/templates/templates.go index b072584827..623fd98f11 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -219,7 +219,7 @@ func NewTemplateExpander(text string, name string, data interface{}, timestamp c return fmt.Sprintf("%.4g%ss", v, prefix) }, "pathPrefix": func() string { - return pathPrefix; + return pathPrefix }, }, } diff --git a/web/api/api.go b/web/api/api.go index 90d1c1453d..046d70f1d7 100644 --- a/web/api/api.go +++ b/web/api/api.go @@ -37,13 +37,13 @@ func (msrv *MetricsService) RegisterHandler(pathPrefix string) { Handler: http.HandlerFunc(h), } } - http.Handle(pathPrefix + "api/query", prometheus.InstrumentHandler( - pathPrefix + "api/query", handler(msrv.Query), + http.Handle(pathPrefix+"api/query", prometheus.InstrumentHandler( + pathPrefix+"api/query", handler(msrv.Query), )) - http.Handle(pathPrefix + "api/query_range", prometheus.InstrumentHandler( - pathPrefix + "api/query_range", handler(msrv.QueryRange), + http.Handle(pathPrefix+"api/query_range", prometheus.InstrumentHandler( + pathPrefix+"api/query_range", handler(msrv.QueryRange), )) - http.Handle(pathPrefix + "api/metrics", prometheus.InstrumentHandler( - pathPrefix + "api/metrics", handler(msrv.Metrics), + http.Handle(pathPrefix+"api/metrics", prometheus.InstrumentHandler( + pathPrefix+"api/metrics", handler(msrv.Metrics), )) } diff --git a/web/consoles.go b/web/consoles.go index d8874aaae0..49346a9172 100644 --- a/web/consoles.go +++ b/web/consoles.go @@ -33,7 +33,7 @@ var ( // ConsolesHandler implements http.Handler. type ConsolesHandler struct { - Storage local.Storage + Storage local.Storage PathPrefix string } diff --git a/web/status.go b/web/status.go index 2e905dd2b4..c641c9aacb 100644 --- a/web/status.go +++ b/web/status.go @@ -32,7 +32,7 @@ type PrometheusStatusHandler struct { RuleManager manager.RuleManager TargetPools map[string]*retrieval.TargetPool - Birth time.Time + Birth time.Time PathPrefix string } diff --git a/web/web.go b/web/web.go index 93a6c952a9..bd53b0de58 100644 --- a/web/web.go +++ b/web/web.go @@ -63,39 +63,39 @@ func (ws WebService) ServeForever(pathPrefix string) error { http.Handle(pathPrefix, prometheus.InstrumentHandler( pathPrefix, ws.StatusHandler, )) - http.Handle(pathPrefix + "alerts", prometheus.InstrumentHandler( - pathPrefix + "alerts", ws.AlertsHandler, + http.Handle(pathPrefix+"alerts", prometheus.InstrumentHandler( + pathPrefix+"alerts", ws.AlertsHandler, )) - http.Handle(pathPrefix + "consoles/", prometheus.InstrumentHandler( - pathPrefix + "consoles/", http.StripPrefix(pathPrefix + "consoles/", ws.ConsolesHandler), + http.Handle(pathPrefix+"consoles/", prometheus.InstrumentHandler( + pathPrefix+"consoles/", http.StripPrefix(pathPrefix+"consoles/", ws.ConsolesHandler), )) - http.Handle(pathPrefix + "graph", prometheus.InstrumentHandler( - pathPrefix + "graph", ws.GraphsHandler, + http.Handle(pathPrefix+"graph", prometheus.InstrumentHandler( + pathPrefix+"graph", ws.GraphsHandler, )) - http.Handle(pathPrefix + "heap", prometheus.InstrumentHandler( - pathPrefix + "heap", http.HandlerFunc(dumpHeap), + http.Handle(pathPrefix+"heap", prometheus.InstrumentHandler( + pathPrefix+"heap", http.HandlerFunc(dumpHeap), )) ws.MetricsHandler.RegisterHandler(pathPrefix) - http.Handle(pathPrefix + strings.TrimLeft(*metricsPath, "/"), prometheus.Handler()) + http.Handle(pathPrefix+strings.TrimLeft(*metricsPath, "/"), prometheus.Handler()) if *useLocalAssets { - http.Handle(pathPrefix + "static/", prometheus.InstrumentHandler( - pathPrefix + "static/", http.StripPrefix(pathPrefix + "static/", http.FileServer(http.Dir("web/static"))), + http.Handle(pathPrefix+"static/", prometheus.InstrumentHandler( + pathPrefix+"static/", http.StripPrefix(pathPrefix+"static/", http.FileServer(http.Dir("web/static"))), )) } else { - http.Handle(pathPrefix + "static/", prometheus.InstrumentHandler( - pathPrefix + "static/", http.StripPrefix(pathPrefix + "static/", new(blob.Handler)), + http.Handle(pathPrefix+"static/", prometheus.InstrumentHandler( + pathPrefix+"static/", http.StripPrefix(pathPrefix+"static/", new(blob.Handler)), )) } if *userAssetsPath != "" { - http.Handle(pathPrefix + "user/", prometheus.InstrumentHandler( - pathPrefix + "user/", http.StripPrefix(pathPrefix + "user/", http.FileServer(http.Dir(*userAssetsPath))), + http.Handle(pathPrefix+"user/", prometheus.InstrumentHandler( + pathPrefix+"user/", http.StripPrefix(pathPrefix+"user/", http.FileServer(http.Dir(*userAssetsPath))), )) } if *enableQuit { - http.Handle(pathPrefix + "-/quit", http.HandlerFunc(ws.quitHandler)) + http.Handle(pathPrefix+"-/quit", http.HandlerFunc(ws.quitHandler)) } if pathPrefix != "/" { From 6c1689f0c64c4580501c604970dded23d0de9572 Mon Sep 17 00:00:00 2001 From: Brian Brazil Date: Fri, 17 Apr 2015 15:49:17 +0100 Subject: [PATCH 03/13] Sort NaN as the lowest value. Currently it ends up in a random position. --- rules/ast/functions.go | 3 +++ rules/rules_test.go | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/rules/ast/functions.go b/rules/ast/functions.go index 5c7ae096aa..7a559ea253 100644 --- a/rules/ast/functions.go +++ b/rules/ast/functions.go @@ -169,6 +169,9 @@ func (s vectorByValueHeap) Len() int { } func (s vectorByValueHeap) Less(i, j int) bool { + if math.IsNaN(float64(s[i].Value)) { + return true + } return s[i].Value < s[j].Value } diff --git a/rules/rules_test.go b/rules/rules_test.go index e19069d59d..32749faa3d 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -307,6 +307,19 @@ func TestExpressions(t *testing.T) { `http_requests{group="canary", instance="1", job="app-server"} => 800 @[%v]`, }, checkOrder: true, + }, { + expr: `sort(0 / round(http_requests, 400) + http_requests)`, + output: []string{ + `{group="production", instance="0", job="api-server"} => NaN @[%v]`, + `{group="production", instance="1", job="api-server"} => 200 @[%v]`, + `{group="canary", instance="0", job="api-server"} => 300 @[%v]`, + `{group="canary", instance="1", job="api-server"} => 400 @[%v]`, + `{group="production", instance="0", job="app-server"} => 500 @[%v]`, + `{group="production", instance="1", job="app-server"} => 600 @[%v]`, + `{group="canary", instance="0", job="app-server"} => 700 @[%v]`, + `{group="canary", instance="1", job="app-server"} => 800 @[%v]`, + }, + checkOrder: true, }, { expr: `sort_desc(http_requests)`, output: []string{ From a0c8b9abbed1e648936084d2d2d8c08717480275 Mon Sep 17 00:00:00 2001 From: rojer Date: Thu, 23 Apr 2015 11:52:08 +0100 Subject: [PATCH 04/13] Make Prepare{Instant,Range}Query public Useful for embedding the query engine. --- rules/ast/ast.go | 4 ++-- rules/ast/printer.go | 4 ++-- rules/ast/query_analyzer.go | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rules/ast/ast.go b/rules/ast/ast.go index eff53ede50..e2bc5ec9b3 100644 --- a/rules/ast/ast.go +++ b/rules/ast/ast.go @@ -392,7 +392,7 @@ func EvalVectorInstant(node VectorNode, timestamp clientmodel.Timestamp, storage totalEvalTimer := queryStats.GetTimer(stats.TotalEvalTime).Start() defer totalEvalTimer.Stop() - closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) + closer, err := PrepareInstantQuery(node, timestamp, storage, queryStats) if err != nil { return nil, err } @@ -412,7 +412,7 @@ func EvalVectorRange(node VectorNode, start clientmodel.Timestamp, end clientmod matrix := Matrix{} prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() - closer, err := prepareRangeQuery(node, start, end, interval, storage, queryStats) + closer, err := PrepareRangeQuery(node, start, end, interval, storage, queryStats) prepareTimer.Stop() if err != nil { return nil, err diff --git a/rules/ast/printer.go b/rules/ast/printer.go index 182f8544a6..93163bf8f0 100644 --- a/rules/ast/printer.go +++ b/rules/ast/printer.go @@ -166,7 +166,7 @@ func EvalToString(node Node, timestamp clientmodel.Timestamp, format OutputForma defer totalEvalTimer.Stop() prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() - closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) + closer, err := PrepareInstantQuery(node, timestamp, storage, queryStats) prepareTimer.Stop() if err != nil { panic(err) @@ -221,7 +221,7 @@ func EvalToVector(node Node, timestamp clientmodel.Timestamp, storage local.Stor defer totalEvalTimer.Stop() prepareTimer := queryStats.GetTimer(stats.TotalQueryPreparationTime).Start() - closer, err := prepareInstantQuery(node, timestamp, storage, queryStats) + closer, err := PrepareInstantQuery(node, timestamp, storage, queryStats) prepareTimer.Stop() if err != nil { panic(err) diff --git a/rules/ast/query_analyzer.go b/rules/ast/query_analyzer.go index 9807f7527d..fe7f2569cd 100644 --- a/rules/ast/query_analyzer.go +++ b/rules/ast/query_analyzer.go @@ -120,7 +120,8 @@ func (i *iteratorInitializer) visit(node Node) { } } -func prepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { +// PrepareInstantQuery analyzes the query and preloads the necessary time range for each series. +func PrepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { totalTimer := queryStats.GetTimer(stats.TotalEvalTime) analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() @@ -167,7 +168,8 @@ func prepareInstantQuery(node Node, timestamp clientmodel.Timestamp, storage loc return p, nil } -func prepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { +// PrepareRangeQuery analyzes the query and preloads the necessary time range for each series. +func PrepareRangeQuery(node Node, start clientmodel.Timestamp, end clientmodel.Timestamp, interval time.Duration, storage local.Storage, queryStats *stats.TimerGroup) (local.Preloader, error) { totalTimer := queryStats.GetTimer(stats.TotalEvalTime) analyzeTimer := queryStats.GetTimer(stats.QueryAnalysisTime).Start() From af99960974d2d75d8fe04a6650ae8cb579b5f559 Mon Sep 17 00:00:00 2001 From: Steve Durrheimer Date: Thu, 23 Apr 2015 21:15:23 +0200 Subject: [PATCH 05/13] Small size docker image refactoring Signed-off-by: Steve Durrheimer --- Dockerfile | 51 ++++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index ade1137eff..03b686007a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,30 +1,35 @@ -FROM golang:1.4 +FROM alpine:edge MAINTAINER The Prometheus Authors -RUN apt-get -qy update && apt-get -qy install vim-common && rm -rf /var/lib/apt/lists/* && \ - go get github.com/tools/godep -WORKDIR /go/src/github.com/prometheus/prometheus -ADD . /go/src/github.com/prometheus/prometheus +ENV GOPATH /go +COPY . /go/src/github.com/prometheus/prometheus -RUN godep restore && go get -d -RUN ./utility/embed-static.sh web/static web/templates | gofmt > web/blob/files.go - -RUN go build -ldflags " \ - -X main.buildVersion $(cat VERSION) \ - -X main.buildRevision $(git rev-parse --short HEAD) \ - -X main.buildBranch $(git rev-parse --abbrev-ref HEAD) \ - -X main.buildUser root \ - -X main.buildDate $(date +%Y%m%d-%H:%M:%S) \ - -X main.goVersion $GOLANG_VERSION \ - " -RUN cd tools/rule_checker && go build -ADD ./documentation/examples/prometheus.conf /prometheus.conf +RUN apk add --update -t build-deps go git mercurial vim \ + && apk add -u musl && rm -rf /var/cache/apk/* \ + && go get github.com/tools/godep \ + && cd /go/src/github.com/prometheus/prometheus \ + && $GOPATH/bin/godep restore && go get -d \ + && ./utility/embed-static.sh web/static web/templates | gofmt > web/blob/files.go \ + && go build -ldflags " \ + -X main.buildVersion $(cat VERSION) \ + -X main.buildRevision $(git rev-parse --short HEAD) \ + -X main.buildBranch $(git rev-parse --abbrev-ref HEAD) \ + -X main.buildUser root \ + -X main.buildDate $(date +%Y%m%d-%H:%M:%S) \ + -X main.goVersion $(go version | awk '{print substr($3,3)}') \ + " -o /bin/prometheus \ + && cd tools/rule_checker && go build && cd ../.. \ + && mkdir -p /etc/prometheus \ + && mv ./documentation/examples/prometheus.conf /etc/prometheus/prometheus.conf \ + && mv ./console_libraries/ ./consoles/ /etc/prometheus/ \ + && rm -rf /go \ + && apk del --purge build-deps EXPOSE 9090 -VOLUME [ "/prometheus" ] +VOLUME [ "/prometheus", "/etc/prometheus" ] WORKDIR /prometheus -ENTRYPOINT [ "/go/src/github.com/prometheus/prometheus/prometheus" ] -CMD [ "-logtostderr", "-config.file=/prometheus.conf", \ +ENTRYPOINT [ "/bin/prometheus" ] +CMD [ "-logtostderr", "-config.file=/etc/prometheus/prometheus.conf", \ "-storage.local.path=/prometheus", \ - "-web.console.libraries=/go/src/github.com/prometheus/prometheus/console_libraries", \ - "-web.console.templates=/go/src/github.com/prometheus/prometheus/consoles" ] + "-web.console.libraries=/etc/prometheus/console_libraries", \ + "-web.console.templates=/etc/prometheus/consoles" ] From 166f492a0e322edf6441ab925d3c26cf0d9c5715 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Fri, 24 Apr 2015 16:12:31 +0200 Subject: [PATCH 06/13] Remove /etc/prometheus from volume definition --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 03b686007a..6d89043877 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ RUN apk add --update -t build-deps go git mercurial vim \ && apk del --purge build-deps EXPOSE 9090 -VOLUME [ "/prometheus", "/etc/prometheus" ] +VOLUME [ "/prometheus" ] WORKDIR /prometheus ENTRYPOINT [ "/bin/prometheus" ] CMD [ "-logtostderr", "-config.file=/etc/prometheus/prometheus.conf", \ From fb3b464f7db90559053876761410ec1f478c6179 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Fri, 24 Apr 2015 17:29:42 +0200 Subject: [PATCH 07/13] Write rule_checker to /bin/rule_checker --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6d89043877..674a9038ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN apk add --update -t build-deps go git mercurial vim \ -X main.buildDate $(date +%Y%m%d-%H:%M:%S) \ -X main.goVersion $(go version | awk '{print substr($3,3)}') \ " -o /bin/prometheus \ - && cd tools/rule_checker && go build && cd ../.. \ + && cd tools/rule_checker && go build -o /bin/rule_checker && cd ../.. \ && mkdir -p /etc/prometheus \ && mv ./documentation/examples/prometheus.conf /etc/prometheus/prometheus.conf \ && mv ./console_libraries/ ./consoles/ /etc/prometheus/ \ From 049b8eaafb33d8322cf33b97c5082c424b86324d Mon Sep 17 00:00:00 2001 From: Fabian Reinartz Date: Thu, 30 Apr 2015 21:40:27 +0200 Subject: [PATCH 08/13] Add *.rules to ignored files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 59ce6935c6..a71bbdb880 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.cgo*.c *.cgo*.go *.conf +*.rules *.exe *.orig *.pyc From e5f92d35fec1ad8702becd2a8dd2dae5a479c709 Mon Sep 17 00:00:00 2001 From: Scott Worley Date: Thu, 30 Apr 2015 14:19:48 -0700 Subject: [PATCH 09/13] Fix storage/local tests for 32-bit systems --- storage/local/codable/codable_test.go | 2 +- storage/local/storage_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/local/codable/codable_test.go b/storage/local/codable/codable_test.go index 501399913a..d5c9cdf83b 100644 --- a/storage/local/codable/codable_test.go +++ b/storage/local/codable/codable_test.go @@ -32,7 +32,7 @@ func newLabelName(ln string) *LabelName { func TestUint64(t *testing.T) { var b bytes.Buffer - const n = 422010471112345 + const n = uint64(422010471112345) if err := EncodeUint64(&b, n); err != nil { t.Fatal(err) } diff --git a/storage/local/storage_test.go b/storage/local/storage_test.go index 6ebbb1a361..9801d6b0e2 100644 --- a/storage/local/storage_test.go +++ b/storage/local/storage_test.go @@ -803,7 +803,7 @@ func createRandomSamples(metricName string, minLen int) clientmodel.Samples { return clientmodel.SampleValue(rand.Intn(1<<16) - 1<<15 + int(v)) }, func(v clientmodel.SampleValue) clientmodel.SampleValue { - return clientmodel.SampleValue(rand.Intn(1<<32) - 1<<31 + int(v)) + return clientmodel.SampleValue(rand.Int63n(1<<32) - 1<<31 + int64(v)) }, }, }, From ab3e08e070a1ecf936832e380bc0817fef840802 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Tue, 5 May 2015 18:24:57 +0200 Subject: [PATCH 10/13] Add rule_checker and console templates to archive --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f17aad6237..5588e87fdb 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ docker: build tarball: $(ARCHIVE) $(ARCHIVE): build - tar -czf $(ARCHIVE) prometheus + tar -czf $(ARCHIVE) prometheus tools/rule_checker/rule_checker consoles console_libraries release: REMOTE ?= $(error "can't upload, REMOTE not set") release: REMOTE_DIR ?= $(error "can't upload, REMOTE_DIR not set") From 36023ed2138f2309dc002998bebbf139db5952f8 Mon Sep 17 00:00:00 2001 From: Johannes 'fish' Ziemke Date: Tue, 5 May 2015 20:29:23 +0200 Subject: [PATCH 11/13] Cut 0.13.2 --- CHANGELOG.md | 10 ++++++++++ VERSION | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0f5ef4b73..1464fb67c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.13.2 / 2015-05-05 +* [MAINTENANCE] Updated vendored dependcies to their newest versions. +* [MAINTENANCE] Include rule_checker and console templates in release tarball. +* [BUGFIX] Sort NaN as the lowest value. +* [ENHANCEMENT] Add square root, stddev and stdvar functions. +* [BUGFIX] Use scrape_timeout for scrape timeout, not scrape_interval. +* [ENHANCEMENT] Improve chunk and chunkDesc loading, increase performance when + reading from disk. +* [BUGFIX] Show correct error on wrong DNS response. + ## 0.13.1 / 2015-04-09 * [BUGFIX] Treat memory series with zero chunks correctly in series maintenance. * [ENHANCEMENT] Improve readability of usage text even more. diff --git a/VERSION b/VERSION index c317a91891..9beb74d490 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.13.1 +0.13.2 From 9820e5fe9984ac18540e8a28eab9d3813d348df3 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Tue, 5 May 2015 18:17:51 +0200 Subject: [PATCH 12/13] Use FastFingerprint where appropriate. --- storage/local/persistence_test.go | 24 ++++++++++++------------ storage/local/series.go | 2 +- storage/local/storage.go | 2 +- storage/local/storage_test.go | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/storage/local/persistence_test.go b/storage/local/persistence_test.go index fc21b8d24a..61abf1b192 100644 --- a/storage/local/persistence_test.go +++ b/storage/local/persistence_test.go @@ -50,9 +50,9 @@ func newTestPersistence(t *testing.T, encoding chunkEncoding) (*persistence, tes func buildTestChunks(encoding chunkEncoding) map[clientmodel.Fingerprint][]chunk { fps := clientmodel.Fingerprints{ - m1.Fingerprint(), - m2.Fingerprint(), - m3.Fingerprint(), + m1.FastFingerprint(), + m2.FastFingerprint(), + m3.FastFingerprint(), } fpToChunks := map[clientmodel.Fingerprint][]chunk{} @@ -375,11 +375,11 @@ func testCheckpointAndLoadSeriesMapAndHeads(t *testing.T, encoding chunkEncoding s5.persistWatermark = 3 chunkCountS4 := len(s4.chunkDescs) chunkCountS5 := len(s5.chunkDescs) - sm.put(m1.Fingerprint(), s1) - sm.put(m2.Fingerprint(), s2) - sm.put(m3.Fingerprint(), s3) - sm.put(m4.Fingerprint(), s4) - sm.put(m5.Fingerprint(), s5) + sm.put(m1.FastFingerprint(), s1) + sm.put(m2.FastFingerprint(), s2) + sm.put(m3.FastFingerprint(), s3) + sm.put(m4.FastFingerprint(), s4) + sm.put(m5.FastFingerprint(), s5) if err := p.checkpointSeriesMapAndHeads(sm, fpLocker); err != nil { t.Fatal(err) @@ -392,7 +392,7 @@ func testCheckpointAndLoadSeriesMapAndHeads(t *testing.T, encoding chunkEncoding if loadedSM.length() != 4 { t.Errorf("want 4 series in map, got %d", loadedSM.length()) } - if loadedS1, ok := loadedSM.get(m1.Fingerprint()); ok { + if loadedS1, ok := loadedSM.get(m1.FastFingerprint()); ok { if !reflect.DeepEqual(loadedS1.metric, m1) { t.Errorf("want metric %v, got %v", m1, loadedS1.metric) } @@ -408,7 +408,7 @@ func testCheckpointAndLoadSeriesMapAndHeads(t *testing.T, encoding chunkEncoding } else { t.Errorf("couldn't find %v in loaded map", m1) } - if loadedS3, ok := loadedSM.get(m3.Fingerprint()); ok { + if loadedS3, ok := loadedSM.get(m3.FastFingerprint()); ok { if !reflect.DeepEqual(loadedS3.metric, m3) { t.Errorf("want metric %v, got %v", m3, loadedS3.metric) } @@ -424,7 +424,7 @@ func testCheckpointAndLoadSeriesMapAndHeads(t *testing.T, encoding chunkEncoding } else { t.Errorf("couldn't find %v in loaded map", m3) } - if loadedS4, ok := loadedSM.get(m4.Fingerprint()); ok { + if loadedS4, ok := loadedSM.get(m4.FastFingerprint()); ok { if !reflect.DeepEqual(loadedS4.metric, m4) { t.Errorf("want metric %v, got %v", m4, loadedS4.metric) } @@ -449,7 +449,7 @@ func testCheckpointAndLoadSeriesMapAndHeads(t *testing.T, encoding chunkEncoding } else { t.Errorf("couldn't find %v in loaded map", m4) } - if loadedS5, ok := loadedSM.get(m5.Fingerprint()); ok { + if loadedS5, ok := loadedSM.get(m5.FastFingerprint()); ok { if !reflect.DeepEqual(loadedS5.metric, m5) { t.Errorf("want metric %v, got %v", m5, loadedS5.metric) } diff --git a/storage/local/series.go b/storage/local/series.go index df76c9712e..5e9c1e5e35 100644 --- a/storage/local/series.go +++ b/storage/local/series.go @@ -318,7 +318,7 @@ func (s *memorySeries) preloadChunks(indexes []int, mss *memorySeriesStorage) ([ if s.chunkDescsOffset == -1 { panic("requested loading chunks from persistence in a situation where we must not have persisted data for chunk descriptors in memory") } - fp := s.metric.Fingerprint() + fp := s.metric.FastFingerprint() // TODO(beorn): Handle collisions. chunks, err := mss.loadChunks(fp, loadIndexes, s.chunkDescsOffset) if err != nil { // Unpin the chunks since we won't return them as pinned chunks now. diff --git a/storage/local/storage.go b/storage/local/storage.go index e741dae611..3782359b6b 100644 --- a/storage/local/storage.go +++ b/storage/local/storage.go @@ -382,7 +382,7 @@ func (s *memorySeriesStorage) Append(sample *clientmodel.Sample) { } glog.Warning("Sample ingestion resumed.") } - fp := sample.Metric.Fingerprint() + fp := sample.Metric.FastFingerprint() // TODO(beorn): Handle collisions. s.fpLocker.Lock(fp) series := s.getOrCreateSeries(fp, sample.Metric) completedChunksCount := series.add(&metric.SamplePair{ diff --git a/storage/local/storage_test.go b/storage/local/storage_test.go index 9801d6b0e2..330a4eafed 100644 --- a/storage/local/storage_test.go +++ b/storage/local/storage_test.go @@ -46,7 +46,7 @@ func TestGetFingerprintsForLabelMatchers(t *testing.T) { Timestamp: clientmodel.Timestamp(i), Value: clientmodel.SampleValue(i), } - fingerprints[i] = metric.Fingerprint() + fingerprints[i] = metric.FastFingerprint() } for _, s := range samples { storage.Append(s) @@ -172,7 +172,7 @@ func TestLoop(t *testing.T) { storage.Append(s) } storage.WaitForIndexing() - series, _ := storage.(*memorySeriesStorage).fpToSeries.get(clientmodel.Metric{}.Fingerprint()) + series, _ := storage.(*memorySeriesStorage).fpToSeries.get(clientmodel.Metric{}.FastFingerprint()) cdsBefore := len(series.chunkDescs) time.Sleep(fpMaxWaitDuration + time.Second) // TODO(beorn7): Ugh, need to wait for maintenance to kick in. cdsAfter := len(series.chunkDescs) @@ -251,7 +251,7 @@ func testGetValueAtTime(t *testing.T, encoding chunkEncoding) { } s.WaitForIndexing() - fp := clientmodel.Metric{}.Fingerprint() + fp := clientmodel.Metric{}.FastFingerprint() it := s.NewIterator(fp) @@ -344,7 +344,7 @@ func testGetRangeValues(t *testing.T, encoding chunkEncoding) { } s.WaitForIndexing() - fp := clientmodel.Metric{}.Fingerprint() + fp := clientmodel.Metric{}.FastFingerprint() it := s.NewIterator(fp) @@ -498,7 +498,7 @@ func testEvictAndPurgeSeries(t *testing.T, encoding chunkEncoding) { } s.WaitForIndexing() - fp := clientmodel.Metric{}.Fingerprint() + fp := clientmodel.Metric{}.FastFingerprint() // Drop ~half of the chunks. ms.maintainMemorySeries(fp, 1000) @@ -896,7 +896,7 @@ func verifyStorage(t testing.TB, s Storage, samples clientmodel.Samples, maxAge // retention period, we can verify here that no results // are returned. } - fp := sample.Metric.Fingerprint() + fp := sample.Metric.FastFingerprint() p := s.NewPreloader() p.PreloadRange(fp, sample.Timestamp, sample.Timestamp, time.Hour) found := s.NewIterator(fp).GetValueAtTime(sample.Timestamp) From bb141fd3db2a485bdca5935907a82453f3b79c96 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Wed, 6 May 2015 12:14:05 +0200 Subject: [PATCH 13/13] Updated vendoring. This pulls in the required changes in client_golang. --- Godeps/Godeps.json | 16 +-- .../client_golang/model/labelname.go | 16 +++ .../prometheus/client_golang/model/metric.go | 68 ++++++++++- .../client_golang/model/metric_test.go | 25 ++-- .../client_golang/model/sample_test.go | 60 ++++------ .../client_golang/model/signature.go | 90 +++++++++++---- .../client_golang/model/signature_test.go | 94 +++++++++++---- .../client_golang/prometheus/counter.go | 2 +- .../client_golang/prometheus/examples_test.go | 108 ++++++++++++++++++ .../client_golang/prometheus/go_collector.go | 19 +++ .../prometheus/go_collector_test.go | 67 ++++++++++- .../client_golang/prometheus/histogram.go | 99 ++++++++++++++++ .../client_golang/prometheus/summary.go | 86 ++++++++++++++ 13 files changed, 642 insertions(+), 108 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1f3b264686..4e05657a7a 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -29,23 +29,23 @@ }, { "ImportPath": "github.com/prometheus/client_golang/extraction", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" + "Comment": "0.5.0", + "Rev": "b0bd7e1be33327b85cb4853e7011156e3cedd657" }, { "ImportPath": "github.com/prometheus/client_golang/model", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" + "Comment": "0.5.0", + "Rev": "b0bd7e1be33327b85cb4853e7011156e3cedd657" }, { "ImportPath": "github.com/prometheus/client_golang/prometheus", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" + "Comment": "0.5.0", + "Rev": "b0bd7e1be33327b85cb4853e7011156e3cedd657" }, { "ImportPath": "github.com/prometheus/client_golang/text", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" + "Comment": "0.5.0", + "Rev": "b0bd7e1be33327b85cb4853e7011156e3cedd657" }, { "ImportPath": "github.com/prometheus/client_model/go", diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go index 75b2e79dae..a283132c59 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go @@ -26,14 +26,30 @@ const ( // timeseries. MetricNameLabel LabelName = "__name__" + // AddressLabel is the name of the label that holds the address of + // a scrape target. + AddressLabel LabelName = "__address__" + + // MetricsPathLabel is the name of the label that holds the path on which to + // scrape a target. + MetricsPathLabel LabelName = "__metrics_path__" + // ReservedLabelPrefix is a prefix which is not legal in user-supplied // label names. ReservedLabelPrefix = "__" + // MetaLabelPrefix is a prefix for labels that provide meta information. + // Labels with this prefix are used for intermediate label processing and + // will not be attached to time series. + MetaLabelPrefix = "__meta_" + // JobLabel is the label name indicating the job from which a timeseries // was scraped. JobLabel LabelName = "job" + // InstanceLabel is the label name used for the instance label. + InstanceLabel LabelName = "instance" + // BucketLabel is used for the label that defines the upper bound of a // bucket of a histogram ("le" -> "less or equal"). BucketLabel = "le" diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go index 32f9d7fbca..0870f23689 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go @@ -26,14 +26,68 @@ var separator = []byte{0} // a singleton and refers to one and only one stream of samples. type Metric map[LabelName]LabelValue -// Equal compares the fingerprints of both metrics. +// Equal compares the metrics. func (m Metric) Equal(o Metric) bool { - return m.Fingerprint().Equal(o.Fingerprint()) + if len(m) != len(o) { + return false + } + for ln, lv := range m { + olv, ok := o[ln] + if !ok { + return false + } + if olv != lv { + return false + } + } + return true } -// Before compares the fingerprints of both metrics. +// Before compares the metrics, using the following criteria: +// +// If m has fewer labels than o, it is before o. If it has more, it is not. +// +// If the number of labels is the same, the superset of all label names is +// sorted alphanumerically. The first differing label pair found in that order +// determines the outcome: If the label does not exist at all in m, then m is +// before o, and vice versa. Otherwise the label value is compared +// alphanumerically. +// +// If m and o are equal, the method returns false. func (m Metric) Before(o Metric) bool { - return m.Fingerprint().Less(o.Fingerprint()) + if len(m) < len(o) { + return true + } + if len(m) > len(o) { + return false + } + + lns := make(LabelNames, 0, len(m)+len(o)) + for ln := range m { + lns = append(lns, ln) + } + for ln := range o { + lns = append(lns, ln) + } + // It's probably not worth it to de-dup lns. + sort.Sort(lns) + for _, ln := range lns { + mlv, ok := m[ln] + if !ok { + return true + } + olv, ok := o[ln] + if !ok { + return false + } + if mlv < olv { + return true + } + if mlv > olv { + return false + } + } + return false } // String implements Stringer. @@ -67,6 +121,12 @@ func (m Metric) Fingerprint() Fingerprint { return metricToFingerprint(m) } +// Fingerprint returns a Metric's Fingerprint calculated by a faster hashing +// algorithm, which is, however, more susceptible to hash collisions. +func (m Metric) FastFingerprint() Fingerprint { + return metricToFastFingerprint(m) +} + // Clone returns a copy of the Metric. func (m Metric) Clone() Metric { clone := Metric{} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go index d51b1842f6..5dbc023764 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go @@ -17,12 +17,14 @@ import "testing" func testMetric(t testing.TB) { var scenarios = []struct { - input Metric - fingerprint Fingerprint + input Metric + fingerprint Fingerprint + fastFingerprint Fingerprint }{ { - input: Metric{}, - fingerprint: 14695981039346656037, + input: Metric{}, + fingerprint: 14695981039346656037, + fastFingerprint: 14695981039346656037, }, { input: Metric{ @@ -30,27 +32,31 @@ func testMetric(t testing.TB) { "occupation": "robot", "manufacturer": "westinghouse", }, - fingerprint: 11310079640881077873, + fingerprint: 5911716720268894962, + fastFingerprint: 11310079640881077873, }, { input: Metric{ "x": "y", }, - fingerprint: 13948396922932177635, + fingerprint: 8241431561484471700, + fastFingerprint: 13948396922932177635, }, { input: Metric{ "a": "bb", "b": "c", }, - fingerprint: 3198632812309449502, + fingerprint: 3016285359649981711, + fastFingerprint: 3198632812309449502, }, { input: Metric{ "a": "b", "bb": "c", }, - fingerprint: 5774953389407657638, + fingerprint: 7122421792099404749, + fastFingerprint: 5774953389407657638, }, } @@ -58,6 +64,9 @@ func testMetric(t testing.TB) { if scenario.fingerprint != scenario.input.Fingerprint() { t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, scenario.input.Fingerprint()) } + if scenario.fastFingerprint != scenario.input.FastFingerprint() { + t.Errorf("%d. expected %d, got %d", i, scenario.fastFingerprint, scenario.input.FastFingerprint()) + } } } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go index 3dc4ad2511..d5e065d6c5 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go @@ -21,42 +21,36 @@ import ( func TestSamplesSort(t *testing.T) { input := Samples{ &Sample{ - // Fingerprint: 81f9c9ed24563f8f. Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 1, }, &Sample{ - // Fingerprint: 81f9c9ed24563f8f. Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 2, }, &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. Metric: Metric{ MetricNameLabel: "C", }, Timestamp: 1, }, &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. Metric: Metric{ MetricNameLabel: "C", }, Timestamp: 2, }, &Sample{ - // Fingerprint: 68f4c9ed24533f8f. Metric: Metric{ MetricNameLabel: "B", }, Timestamp: 1, }, &Sample{ - // Fingerprint: 68f4c9ed24533f8f. Metric: Metric{ MetricNameLabel: "B", }, @@ -66,47 +60,41 @@ func TestSamplesSort(t *testing.T) { expected := Samples{ &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 2, - }, - &Sample{ - // Fingerprint: 68f4c9ed24533f8f. - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 68f4c9ed24533f8f. - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 2, - }, - &Sample{ - // Fingerprint: 81f9c9ed24563f8f. Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 1, }, &Sample{ - // Fingerprint: 81f9c9ed24563f8f. Metric: Metric{ MetricNameLabel: "A", }, Timestamp: 2, }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "B", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "B", + }, + Timestamp: 2, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "C", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "C", + }, + Timestamp: 2, + }, } sort.Sort(input) diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go index cc77b192dd..7bd58f4b0b 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go @@ -17,6 +17,7 @@ import ( "bytes" "hash" "hash/fnv" + "sort" "sync" ) @@ -46,30 +47,37 @@ func getHashAndBuf() *hashAndBuf { } func putHashAndBuf(hb *hashAndBuf) { + hb.h.Reset() + hb.b.Reset() hashAndBufPool.Put(hb) } -// LabelsToSignature returns a unique signature (i.e., fingerprint) for a given -// label set. +// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a +// given label set. (Collisions are possible but unlikely if the number of label +// sets the function is applied to is small.) func LabelsToSignature(labels map[string]string) uint64 { if len(labels) == 0 { return emptyLabelSignature } - var result uint64 + labelNames := make([]string, 0, len(labels)) + for labelName := range labels { + labelNames = append(labelNames, labelName) + } + sort.Strings(labelNames) + hb := getHashAndBuf() defer putHashAndBuf(hb) - for labelName, labelValue := range labels { + for _, labelName := range labelNames { hb.b.WriteString(labelName) hb.b.WriteByte(SeparatorByte) - hb.b.WriteString(labelValue) + hb.b.WriteString(labels[labelName]) + hb.b.WriteByte(SeparatorByte) hb.h.Write(hb.b.Bytes()) - result ^= hb.h.Sum64() - hb.h.Reset() hb.b.Reset() } - return result + return hb.h.Sum64() } // metricToFingerprint works exactly as LabelsToSignature but takes a Metric as @@ -79,6 +87,34 @@ func metricToFingerprint(m Metric) Fingerprint { return Fingerprint(emptyLabelSignature) } + labelNames := make(LabelNames, 0, len(m)) + for labelName := range m { + labelNames = append(labelNames, labelName) + } + sort.Sort(labelNames) + + hb := getHashAndBuf() + defer putHashAndBuf(hb) + + for _, labelName := range labelNames { + hb.b.WriteString(string(labelName)) + hb.b.WriteByte(SeparatorByte) + hb.b.WriteString(string(m[labelName])) + hb.b.WriteByte(SeparatorByte) + hb.h.Write(hb.b.Bytes()) + hb.b.Reset() + } + return Fingerprint(hb.h.Sum64()) +} + +// metricToFastFingerprint works similar to metricToFingerprint but uses a +// faster and less allocation-heavy hash function, which is more susceptible to +// create hash collisions. Therefore, collision detection should be applied. +func metricToFastFingerprint(m Metric) Fingerprint { + if len(m) == 0 { + return Fingerprint(emptyLabelSignature) + } + var result uint64 hb := getHashAndBuf() defer putHashAndBuf(hb) @@ -97,13 +133,15 @@ func metricToFingerprint(m Metric) Fingerprint { // SignatureForLabels works like LabelsToSignature but takes a Metric as // parameter (rather than a label map) and only includes the labels with the -// specified LabelNames into the signature calculation. +// specified LabelNames into the signature calculation. The labels passed in +// will be sorted by this function. func SignatureForLabels(m Metric, labels LabelNames) uint64 { if len(m) == 0 || len(labels) == 0 { return emptyLabelSignature } - var result uint64 + sort.Sort(labels) + hb := getHashAndBuf() defer putHashAndBuf(hb) @@ -111,12 +149,11 @@ func SignatureForLabels(m Metric, labels LabelNames) uint64 { hb.b.WriteString(string(label)) hb.b.WriteByte(SeparatorByte) hb.b.WriteString(string(m[label])) + hb.b.WriteByte(SeparatorByte) hb.h.Write(hb.b.Bytes()) - result ^= hb.h.Sum64() - hb.h.Reset() hb.b.Reset() } - return result + return hb.h.Sum64() } // SignatureWithoutLabels works like LabelsToSignature but takes a Metric as @@ -127,24 +164,27 @@ func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 { return emptyLabelSignature } - var result uint64 + labelNames := make(LabelNames, 0, len(m)) + for labelName := range m { + if _, exclude := labels[labelName]; !exclude { + labelNames = append(labelNames, labelName) + } + } + if len(labelNames) == 0 { + return emptyLabelSignature + } + sort.Sort(labelNames) + hb := getHashAndBuf() defer putHashAndBuf(hb) - for labelName, labelValue := range m { - if _, exclude := labels[labelName]; exclude { - continue - } + for _, labelName := range labelNames { hb.b.WriteString(string(labelName)) hb.b.WriteByte(SeparatorByte) - hb.b.WriteString(string(labelValue)) + hb.b.WriteString(string(m[labelName])) + hb.b.WriteByte(SeparatorByte) hb.h.Write(hb.b.Bytes()) - result ^= hb.h.Sum64() - hb.h.Reset() hb.b.Reset() } - if result == 0 { - return emptyLabelSignature - } - return result + return hb.h.Sum64() } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go index 7b3327d440..01db531d07 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go @@ -30,7 +30,7 @@ func TestLabelsToSignature(t *testing.T) { }, { in: map[string]string{"name": "garland, briggs", "fear": "love is not enough"}, - out: 12952432476264840823, + out: 5799056148416392346, }, } @@ -54,7 +54,7 @@ func TestMetricToFingerprint(t *testing.T) { }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, - out: 12952432476264840823, + out: 5799056148416392346, }, } @@ -67,6 +67,30 @@ func TestMetricToFingerprint(t *testing.T) { } } +func TestMetricToFastFingerprint(t *testing.T) { + var scenarios = []struct { + in Metric + out Fingerprint + }{ + { + in: Metric{}, + out: 14695981039346656037, + }, + { + in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, + out: 12952432476264840823, + }, + } + + for i, scenario := range scenarios { + actual := metricToFastFingerprint(scenario.in) + + if actual != scenario.out { + t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) + } + } +} + func TestSignatureForLabels(t *testing.T) { var scenarios = []struct { in Metric @@ -81,12 +105,12 @@ func TestSignatureForLabels(t *testing.T) { { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: LabelNames{"fear", "name"}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, labels: LabelNames{"fear", "name"}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, @@ -128,17 +152,17 @@ func TestSignatureWithoutLabels(t *testing.T) { { in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, labels: map[LabelName]struct{}{"foo": struct{}{}}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: map[LabelName]struct{}{}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: nil, - out: 12952432476264840823, + out: 5799056148416392346, }, } @@ -164,15 +188,15 @@ func BenchmarkLabelToSignatureScalar(b *testing.B) { } func BenchmarkLabelToSignatureSingle(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5147259542624943964) + benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5146282821936882169) } func BenchmarkLabelToSignatureDouble(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) + benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) } func BenchmarkLabelToSignatureTriple(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) + benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) } func benchmarkMetricToFingerprint(b *testing.B, m Metric, e Fingerprint) { @@ -188,15 +212,39 @@ func BenchmarkMetricToFingerprintScalar(b *testing.B) { } func BenchmarkMetricToFingerprintSingle(b *testing.B) { - benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value"}, 5147259542624943964) + benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value"}, 5146282821936882169) } func BenchmarkMetricToFingerprintDouble(b *testing.B) { - benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) + benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) } func BenchmarkMetricToFingerprintTriple(b *testing.B) { - benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) + benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) +} + +func benchmarkMetricToFastFingerprint(b *testing.B, m Metric, e Fingerprint) { + for i := 0; i < b.N; i++ { + if a := metricToFastFingerprint(m); a != e { + b.Fatalf("expected signature of %d for %s, got %d", e, m, a) + } + } +} + +func BenchmarkMetricToFastFingerprintScalar(b *testing.B) { + benchmarkMetricToFastFingerprint(b, nil, 14695981039346656037) +} + +func BenchmarkMetricToFastFingerprintSingle(b *testing.B) { + benchmarkMetricToFastFingerprint(b, Metric{"first-label": "first-label-value"}, 5147259542624943964) +} + +func BenchmarkMetricToFastFingerprintDouble(b *testing.B) { + benchmarkMetricToFastFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) +} + +func BenchmarkMetricToFastFingerprintTriple(b *testing.B) { + benchmarkMetricToFastFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) } func TestEmptyLabelSignature(t *testing.T) { @@ -218,7 +266,7 @@ func TestEmptyLabelSignature(t *testing.T) { } } -func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, concLevel int) { +func benchmarkMetricToFastFingerprintConc(b *testing.B, m Metric, e Fingerprint, concLevel int) { var start, end sync.WaitGroup start.Add(1) end.Add(concLevel) @@ -227,7 +275,7 @@ func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, con go func() { start.Wait() for j := b.N / concLevel; j >= 0; j-- { - if a := metricToFingerprint(m); a != e { + if a := metricToFastFingerprint(m); a != e { b.Fatalf("expected signature of %d for %s, got %d", e, m, a) } } @@ -239,18 +287,18 @@ func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, con end.Wait() } -func BenchmarkMetricToFingerprintTripleConc1(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1) +func BenchmarkMetricToFastFingerprintTripleConc1(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1) } -func BenchmarkMetricToFingerprintTripleConc2(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2) +func BenchmarkMetricToFastFingerprintTripleConc2(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2) } -func BenchmarkMetricToFingerprintTripleConc4(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4) +func BenchmarkMetricToFastFingerprintTripleConc4(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4) } -func BenchmarkMetricToFingerprintTripleConc8(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8) +func BenchmarkMetricToFastFingerprintTripleConc8(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8) } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go index f8d633fbd5..a2952d1c88 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go @@ -33,7 +33,7 @@ type Counter interface { // Set is used to set the Counter to an arbitrary value. It is only used // if you have to transfer a value from an external counter into this - // Prometheus metrics. Do not use it for regular handling of a + // Prometheus metric. Do not use it for regular handling of a // Prometheus counter (as it can be used to break the contract of // monotonically increasing values). Set(float64) diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go index d106c42c6b..a28a80125f 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go @@ -455,6 +455,56 @@ func ExampleSummaryVec() { // ] } +func ExampleConstSummary() { + desc := prometheus.NewDesc( + "http_request_duration_seconds", + "A summary of the HTTP request durations.", + []string{"code", "method"}, + prometheus.Labels{"owner": "example"}, + ) + + // Create a constant summary from values we got from a 3rd party telemetry system. + s := prometheus.MustNewConstSummary( + desc, + 4711, 403.34, + map[float64]float64{0.5: 42.3, 0.9: 323.3}, + "200", "get", + ) + + // Just for demonstration, let's check the state of the summary by + // (ab)using its Write method (which is usually only used by Prometheus + // internally). + metric := &dto.Metric{} + s.Write(metric) + fmt.Println(proto.MarshalTextString(metric)) + + // Output: + // label: < + // name: "code" + // value: "200" + // > + // label: < + // name: "method" + // value: "get" + // > + // label: < + // name: "owner" + // value: "example" + // > + // summary: < + // sample_count: 4711 + // sample_sum: 403.34 + // quantile: < + // quantile: 0.5 + // value: 42.3 + // > + // quantile: < + // quantile: 0.9 + // value: 323.3 + // > + // > +} + func ExampleHistogram() { temps := prometheus.NewHistogram(prometheus.HistogramOpts{ Name: "pond_temperature_celsius", @@ -501,6 +551,64 @@ func ExampleHistogram() { // > } +func ExampleConstHistogram() { + desc := prometheus.NewDesc( + "http_request_duration_seconds", + "A histogram of the HTTP request durations.", + []string{"code", "method"}, + prometheus.Labels{"owner": "example"}, + ) + + // Create a constant histogram from values we got from a 3rd party telemetry system. + h := prometheus.MustNewConstHistogram( + desc, + 4711, 403.34, + map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, + "200", "get", + ) + + // Just for demonstration, let's check the state of the histogram by + // (ab)using its Write method (which is usually only used by Prometheus + // internally). + metric := &dto.Metric{} + h.Write(metric) + fmt.Println(proto.MarshalTextString(metric)) + + // Output: + // label: < + // name: "code" + // value: "200" + // > + // label: < + // name: "method" + // value: "get" + // > + // label: < + // name: "owner" + // value: "example" + // > + // histogram: < + // sample_count: 4711 + // sample_sum: 403.34 + // bucket: < + // cumulative_count: 121 + // upper_bound: 25 + // > + // bucket: < + // cumulative_count: 2403 + // upper_bound: 50 + // > + // bucket: < + // cumulative_count: 3221 + // upper_bound: 100 + // > + // bucket: < + // cumulative_count: 4233 + // upper_bound: 200 + // > + // > +} + func ExamplePushCollectors() { hostname, _ := os.Hostname() completionTime := prometheus.NewGauge(prometheus.GaugeOpts{ diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go index d7b7a20a12..dbf4c306fb 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go @@ -2,10 +2,13 @@ package prometheus import ( "runtime" + "runtime/debug" + "time" ) type goCollector struct { goroutines Gauge + gcDesc *Desc } // NewGoCollector returns a collector which exports metrics about the current @@ -16,16 +19,32 @@ func NewGoCollector() *goCollector { Name: "process_goroutines", Help: "Number of goroutines that currently exist.", }), + gcDesc: NewDesc( + "go_gc_duration_seconds", + "A summary of the GC invocation durations.", + nil, nil), } } // Describe returns all descriptions of the collector. func (c *goCollector) Describe(ch chan<- *Desc) { ch <- c.goroutines.Desc() + ch <- c.gcDesc } // Collect returns the current state of all metrics of the collector. func (c *goCollector) Collect(ch chan<- Metric) { c.goroutines.Set(float64(runtime.NumGoroutine())) ch <- c.goroutines + + var stats debug.GCStats + stats.PauseQuantiles = make([]time.Duration, 5) + debug.ReadGCStats(&stats) + + quantiles := make(map[float64]float64) + for idx, pq := range stats.PauseQuantiles[1:] { + quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds() + } + quantiles[0.0] = stats.PauseQuantiles[0].Seconds() + ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles) } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go index b0582d1b98..b75d28e593 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go @@ -1,7 +1,7 @@ package prometheus import ( - "reflect" + "runtime" "testing" "time" @@ -35,6 +35,9 @@ func TestGoCollector(t *testing.T) { case Gauge: pb := &dto.Metric{} m.Write(pb) + if pb.GetGauge() == nil { + continue + } if old == -1 { old = int(pb.GetGauge().GetValue()) @@ -48,8 +51,66 @@ func TestGoCollector(t *testing.T) { } return - default: - t.Errorf("want type Gauge, got %s", reflect.TypeOf(metric)) + } + case <-time.After(1 * time.Second): + t.Fatalf("expected collect timed out") + } + } +} + +func TestGCCollector(t *testing.T) { + var ( + c = NewGoCollector() + ch = make(chan Metric) + waitc = make(chan struct{}) + closec = make(chan struct{}) + oldGC uint64 + oldPause float64 + ) + defer close(closec) + + go func() { + c.Collect(ch) + // force GC + runtime.GC() + <-waitc + c.Collect(ch) + }() + + first := true + for { + select { + case metric := <-ch: + switch m := metric.(type) { + case *constSummary, *value: + pb := &dto.Metric{} + m.Write(pb) + if pb.GetSummary() == nil { + continue + } + + if len(pb.GetSummary().Quantile) != 5 { + t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile)) + } + for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} { + if *pb.GetSummary().Quantile[idx].Quantile != want { + t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want) + } + } + if first { + first = false + oldGC = *pb.GetSummary().SampleCount + oldPause = *pb.GetSummary().SampleSum + close(waitc) + continue + } + if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 { + t.Errorf("want 1 new garbage collection run, got %d", diff) + } + if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 { + t.Errorf("want moar pause, got %f", diff) + } + return } case <-time.After(1 * time.Second): t.Fatalf("expected collect timed out") diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go index 9b36a61150..27e9c5f7d2 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go @@ -342,3 +342,102 @@ func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram { func (m *HistogramVec) With(labels Labels) Histogram { return m.MetricVec.With(labels).(Histogram) } + +type constHistogram struct { + desc *Desc + count uint64 + sum float64 + buckets map[float64]uint64 + labelPairs []*dto.LabelPair +} + +func (h *constHistogram) Desc() *Desc { + return h.desc +} + +func (h *constHistogram) Write(out *dto.Metric) error { + his := &dto.Histogram{} + buckets := make([]*dto.Bucket, 0, len(h.buckets)) + + his.SampleCount = proto.Uint64(h.count) + his.SampleSum = proto.Float64(h.sum) + + for upperBound, count := range h.buckets { + buckets = append(buckets, &dto.Bucket{ + CumulativeCount: proto.Uint64(count), + UpperBound: proto.Float64(upperBound), + }) + } + + if len(buckets) > 0 { + sort.Sort(buckSort(buckets)) + } + his.Bucket = buckets + + out.Histogram = his + out.Label = h.labelPairs + + return nil +} + +// NewConstHistogram returns a metric representing a Prometheus histogram with +// fixed values for the count, sum, and bucket counts. As those parameters +// cannot be changed, the returned value does not implement the Histogram +// interface (but only the Metric interface). Users of this package will not +// have much use for it in regular operations. However, when implementing custom +// Collectors, it is useful as a throw-away metric that is generated on the fly +// to send it to Prometheus in the Collect method. +// +// buckets is a map of upper bounds to cumulative counts, excluding the +Inf +// bucket. +// +// NewConstHistogram returns an error if the length of labelValues is not +// consistent with the variable labels in Desc. +func NewConstHistogram( + desc *Desc, + count uint64, + sum float64, + buckets map[float64]uint64, + labelValues ...string, +) (Metric, error) { + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality + } + return &constHistogram{ + desc: desc, + count: count, + sum: sum, + buckets: buckets, + labelPairs: makeLabelPairs(desc, labelValues), + }, nil +} + +// MustNewConstHistogram is a version of NewConstHistogram that panics where +// NewConstMetric would have returned an error. +func MustNewConstHistogram( + desc *Desc, + count uint64, + sum float64, + buckets map[float64]uint64, + labelValues ...string, +) Metric { + m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...) + if err != nil { + panic(err) + } + return m +} + +type buckSort []*dto.Bucket + +func (s buckSort) Len() int { + return len(s) +} + +func (s buckSort) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s buckSort) Less(i, j int) bool { + return s[i].GetUpperBound() < s[j].GetUpperBound() +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go index dd336c5199..67fe43cd74 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go @@ -448,3 +448,89 @@ func (m *SummaryVec) WithLabelValues(lvs ...string) Summary { func (m *SummaryVec) With(labels Labels) Summary { return m.MetricVec.With(labels).(Summary) } + +type constSummary struct { + desc *Desc + count uint64 + sum float64 + quantiles map[float64]float64 + labelPairs []*dto.LabelPair +} + +func (s *constSummary) Desc() *Desc { + return s.desc +} + +func (s *constSummary) Write(out *dto.Metric) error { + sum := &dto.Summary{} + qs := make([]*dto.Quantile, 0, len(s.quantiles)) + + sum.SampleCount = proto.Uint64(s.count) + sum.SampleSum = proto.Float64(s.sum) + + for rank, q := range s.quantiles { + qs = append(qs, &dto.Quantile{ + Quantile: proto.Float64(rank), + Value: proto.Float64(q), + }) + } + + if len(qs) > 0 { + sort.Sort(quantSort(qs)) + } + sum.Quantile = qs + + out.Summary = sum + out.Label = s.labelPairs + + return nil +} + +// NewConstSummary returns a metric representing a Prometheus summary with fixed +// values for the count, sum, and quantiles. As those parameters cannot be +// changed, the returned value does not implement the Summary interface (but +// only the Metric interface). Users of this package will not have much use for +// it in regular operations. However, when implementing custom Collectors, it is +// useful as a throw-away metric that is generated on the fly to send it to +// Prometheus in the Collect method. +// +// quantiles maps ranks to quantile values. For example, a median latency of +// 0.23s and a 99th percentile latency of 0.56s would be expressed as: +// map[float64]float64{0.5: 0.23, 0.99: 0.56} +// +// NewConstSummary returns an error if the length of labelValues is not +// consistent with the variable labels in Desc. +func NewConstSummary( + desc *Desc, + count uint64, + sum float64, + quantiles map[float64]float64, + labelValues ...string, +) (Metric, error) { + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality + } + return &constSummary{ + desc: desc, + count: count, + sum: sum, + quantiles: quantiles, + labelPairs: makeLabelPairs(desc, labelValues), + }, nil +} + +// MustNewConstSummary is a version of NewConstSummary that panics where +// NewConstMetric would have returned an error. +func MustNewConstSummary( + desc *Desc, + count uint64, + sum float64, + quantiles map[float64]float64, + labelValues ...string, +) Metric { + m, err := NewConstSummary(desc, count, sum, quantiles, labelValues...) + if err != nil { + panic(err) + } + return m +}