mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-12 14:27:27 -08:00
44fcf876ca
* Implement Pretty() function for AST nodes. Signed-off-by: Harkishen-Singh <harkishensingh@hotmail.com> This commit adds .Pretty() for all nodes of PromQL AST. Each .Pretty() prettifies the node it belongs to, and under no circustance, the parent or child node is touch/prettified. Read more in the "Approach" part in `prettier.go` * Refactor functions between printer.go & prettier.go Signed-off-by: Harkishen-Singh <harkishensingh@hotmail.com> This commit removes redundancy between printer.go and prettier.go by taking out the common code into separate private functions. * Add more unit tests for Prettier. Signed-off-by: Harkishen-Singh <harkishensingh@hotmail.com> * Add support for spliting function calls with 1 arg & unary expressions. Signed-off-by: Harkishen-Singh <harkishensingh@hotmail.com> This commit does 2 things: 1. It adds support to split function calls that have 1 arg and exceeds the max_characters_per_line to multiple lines. 2. Splits Unary expressions that exceed the max_characters_per_line. This is done by formatting the child node and then removing the prefix indent, which is already applied before the unary operator.
667 lines
12 KiB
Go
667 lines
12 KiB
Go
// Copyright 2022 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAggregateExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `sum(foo)`,
|
|
out: `sum(foo)`,
|
|
},
|
|
{
|
|
in: `sum by() (task:errors:rate10s{job="s"})`,
|
|
out: `sum(
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum without(job,foo) (task:errors:rate10s{job="s"})`,
|
|
out: `sum without(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum(task:errors:rate10s{job="s"}) without(job,foo)`,
|
|
out: `sum without(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) (task:errors:rate10s{job="s"})`,
|
|
out: `sum by(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum (task:errors:rate10s{job="s"}) by(job,foo)`,
|
|
out: `sum by(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `topk(10, ask:errors:rate10s{job="s"})`,
|
|
out: `topk(
|
|
10,
|
|
ask:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"}))`,
|
|
out: `sum by(job, foo) (
|
|
sum by(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) (sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"})))`,
|
|
out: `sum by(job, foo) (
|
|
sum by(job, foo) (
|
|
sum by(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo)
|
|
(sum by(job,foo) (task:errors:rate10s{job="s"}))`,
|
|
out: `sum by(job, foo) (
|
|
sum by(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo)
|
|
(sum(task:errors:rate10s{job="s"}) without(job,foo))`,
|
|
out: `sum by(job, foo) (
|
|
sum without(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) # Comment 1.
|
|
(sum by(job,foo) ( # Comment 2.
|
|
task:errors:rate10s{job="s"}))`,
|
|
out: `sum by(job, foo) (
|
|
sum by(job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestBinaryExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `a+b`,
|
|
out: `a + b`,
|
|
},
|
|
{
|
|
in: `a == bool 1`,
|
|
out: ` a
|
|
== bool
|
|
1`,
|
|
},
|
|
{
|
|
in: `a + ignoring(job) b`,
|
|
out: ` a
|
|
+ ignoring(job)
|
|
b`,
|
|
},
|
|
{
|
|
in: `foo_1 + foo_2`,
|
|
out: ` foo_1
|
|
+
|
|
foo_2`,
|
|
},
|
|
{
|
|
in: `foo_1 + foo_2 + foo_3`,
|
|
out: ` foo_1
|
|
+
|
|
foo_2
|
|
+
|
|
foo_3`,
|
|
},
|
|
{
|
|
in: `foo + baar + foo_3`,
|
|
out: ` foo + baar
|
|
+
|
|
foo_3`,
|
|
},
|
|
{
|
|
in: `foo_1 + foo_2 + foo_3 + foo_4`,
|
|
out: ` foo_1
|
|
+
|
|
foo_2
|
|
+
|
|
foo_3
|
|
+
|
|
foo_4`,
|
|
},
|
|
{
|
|
in: `foo_1 + ignoring(foo) foo_2 + ignoring(job) group_left foo_3 + on(instance) group_right foo_4`,
|
|
out: ` foo_1
|
|
+ ignoring(foo)
|
|
foo_2
|
|
+ ignoring(job) group_left()
|
|
foo_3
|
|
+ on(instance) group_right()
|
|
foo_4`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestCallExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `rate(foo[1m])`,
|
|
out: `rate(
|
|
foo[1m]
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum_over_time(foo[1m])`,
|
|
out: `sum_over_time(
|
|
foo[1m]
|
|
)`,
|
|
},
|
|
{
|
|
in: `rate(long_vector_selector[10m:1m] @ start() offset 1m)`,
|
|
out: `rate(
|
|
long_vector_selector[10m:1m] @ start() offset 1m
|
|
)`,
|
|
},
|
|
{
|
|
in: `histogram_quantile(0.9, rate(foo[1m]))`,
|
|
out: `histogram_quantile(
|
|
0.9,
|
|
rate(
|
|
foo[1m]
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `max_over_time(rate(demo_api_request_duration_seconds_count[1m])[1m:] @ start() offset 1m)`,
|
|
out: `max_over_time(
|
|
rate(
|
|
demo_api_request_duration_seconds_count[1m]
|
|
)[1m:] @ start() offset 1m
|
|
)`,
|
|
},
|
|
{
|
|
in: `label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")`,
|
|
out: `label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)`,
|
|
},
|
|
{
|
|
in: `label_replace(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*")`,
|
|
out: `label_replace(
|
|
label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
fmt.Println("=>", expr.String())
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestParenExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `(foo)`,
|
|
out: `(foo)`,
|
|
},
|
|
{
|
|
in: `(_foo_long_)`,
|
|
out: `(
|
|
_foo_long_
|
|
)`,
|
|
},
|
|
{
|
|
in: `((foo_long))`,
|
|
out: `(
|
|
(foo_long)
|
|
)`,
|
|
},
|
|
{
|
|
in: `((_foo_long_))`,
|
|
out: `(
|
|
(
|
|
_foo_long_
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(((foo_long)))`,
|
|
out: `(
|
|
(
|
|
(foo_long)
|
|
)
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestStepInvariantExpr(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `a @ 1`,
|
|
out: `a @ 1.000`,
|
|
},
|
|
{
|
|
in: `a @ start()`,
|
|
out: `a @ start()`,
|
|
},
|
|
{
|
|
in: `vector_selector @ start()`,
|
|
out: `vector_selector @ start()`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `(1 + 2)`,
|
|
out: `(1 + 2)`,
|
|
},
|
|
{
|
|
in: `(foo + bar)`,
|
|
out: `(
|
|
foo + bar
|
|
)`,
|
|
},
|
|
{
|
|
in: `(foo_long + bar_long)`,
|
|
out: `(
|
|
foo_long
|
|
+
|
|
bar_long
|
|
)`,
|
|
},
|
|
{
|
|
in: `(foo_long + bar_long + bar_2_long)`,
|
|
out: `(
|
|
foo_long
|
|
+
|
|
bar_long
|
|
+
|
|
bar_2_long
|
|
)`,
|
|
},
|
|
{
|
|
in: `((foo_long + bar_long) + bar_2_long)`,
|
|
out: `(
|
|
(
|
|
foo_long
|
|
+
|
|
bar_long
|
|
)
|
|
+
|
|
bar_2_long
|
|
)`,
|
|
},
|
|
{
|
|
in: `(1111 + 2222)`,
|
|
out: `(
|
|
1111
|
|
+
|
|
2222
|
|
)`,
|
|
},
|
|
{
|
|
in: `(sum_over_time(foo[1m]))`,
|
|
out: `(
|
|
sum_over_time(
|
|
foo[1m]
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `histogram_quantile(0.9, rate(foo[1m] @ start()))`,
|
|
out: `histogram_quantile(
|
|
0.9,
|
|
rate(
|
|
foo[1m] @ start()
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"))`,
|
|
out: `(
|
|
label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(label_replace(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*"))`,
|
|
out: `(
|
|
label_replace(
|
|
label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(label_replace(label_replace((up{job="api-server",service="a:c"}), "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*"))`,
|
|
out: `(
|
|
label_replace(
|
|
label_replace(
|
|
(
|
|
up{job="api-server",service="a:c"}
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)
|
|
)`,
|
|
},
|
|
// Following queries have been taken from https://monitoring.mixins.dev/
|
|
{
|
|
in: `(node_filesystem_avail_bytes{job="node",fstype!=""} / node_filesystem_size_bytes{job="node",fstype!=""} * 100 < 40 and predict_linear(node_filesystem_avail_bytes{job="node",fstype!=""}[6h], 24*60*60) < 0 and node_filesystem_readonly{job="node",fstype!=""} == 0)`,
|
|
out: `(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}
|
|
/
|
|
node_filesystem_size_bytes{fstype!="",job="node"}
|
|
*
|
|
100
|
|
<
|
|
40
|
|
and
|
|
predict_linear(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}[6h],
|
|
24 * 60
|
|
*
|
|
60
|
|
)
|
|
<
|
|
0
|
|
and
|
|
node_filesystem_readonly{fstype!="",job="node"}
|
|
==
|
|
0
|
|
)`,
|
|
},
|
|
{
|
|
in: `(node_filesystem_avail_bytes{job="node",fstype!=""} / node_filesystem_size_bytes{job="node",fstype!=""} * 100 < 20 and predict_linear(node_filesystem_avail_bytes{job="node",fstype!=""}[6h], 4*60*60) < 0 and node_filesystem_readonly{job="node",fstype!=""} == 0)`,
|
|
out: `(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}
|
|
/
|
|
node_filesystem_size_bytes{fstype!="",job="node"}
|
|
*
|
|
100
|
|
<
|
|
20
|
|
and
|
|
predict_linear(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}[6h],
|
|
4 * 60
|
|
*
|
|
60
|
|
)
|
|
<
|
|
0
|
|
and
|
|
node_filesystem_readonly{fstype!="",job="node"}
|
|
==
|
|
0
|
|
)`,
|
|
},
|
|
{
|
|
in: `(node_timex_offset_seconds > 0.05 and deriv(node_timex_offset_seconds[5m]) >= 0) or (node_timex_offset_seconds < -0.05 and deriv(node_timex_offset_seconds[5m]) <= 0)`,
|
|
out: ` (
|
|
node_timex_offset_seconds
|
|
>
|
|
0.05
|
|
and
|
|
deriv(
|
|
node_timex_offset_seconds[5m]
|
|
)
|
|
>=
|
|
0
|
|
)
|
|
or
|
|
(
|
|
node_timex_offset_seconds
|
|
<
|
|
-0.05
|
|
and
|
|
deriv(
|
|
node_timex_offset_seconds[5m]
|
|
)
|
|
<=
|
|
0
|
|
)`,
|
|
},
|
|
{
|
|
in: `1 - ((node_memory_MemAvailable_bytes{job="node"} or (node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"}) ) / node_memory_MemTotal_bytes{job="node"})`,
|
|
out: ` 1
|
|
-
|
|
(
|
|
(
|
|
node_memory_MemAvailable_bytes{job="node"}
|
|
or
|
|
(
|
|
node_memory_Buffers_bytes{job="node"}
|
|
+
|
|
node_memory_Cached_bytes{job="node"}
|
|
+
|
|
node_memory_MemFree_bytes{job="node"}
|
|
+
|
|
node_memory_Slab_bytes{job="node"}
|
|
)
|
|
)
|
|
/
|
|
node_memory_MemTotal_bytes{job="node"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `min by (job, integration) (rate(alertmanager_notifications_failed_total{job="alertmanager", integration=~".*"}[5m]) / rate(alertmanager_notifications_total{job="alertmanager", integration="~.*"}[5m])) > 0.01`,
|
|
out: ` min by(job, integration) (
|
|
rate(
|
|
alertmanager_notifications_failed_total{integration=~".*",job="alertmanager"}[5m]
|
|
)
|
|
/
|
|
rate(
|
|
alertmanager_notifications_total{integration="~.*",job="alertmanager"}[5m]
|
|
)
|
|
)
|
|
>
|
|
0.01`,
|
|
},
|
|
{
|
|
in: `(count by (job) (changes(process_start_time_seconds{job="alertmanager"}[10m]) > 4) / count by (job) (up{job="alertmanager"})) >= 0.5`,
|
|
out: ` (
|
|
count by(job) (
|
|
changes(
|
|
process_start_time_seconds{job="alertmanager"}[10m]
|
|
)
|
|
>
|
|
4
|
|
)
|
|
/
|
|
count by(job) (
|
|
up{job="alertmanager"}
|
|
)
|
|
)
|
|
>=
|
|
0.5`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestUnaryPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `-1`,
|
|
out: `-1`,
|
|
},
|
|
{
|
|
in: `-vector_selector`,
|
|
out: `-vector_selector`,
|
|
},
|
|
{
|
|
in: `(-vector_selector)`,
|
|
out: `(
|
|
-vector_selector
|
|
)`,
|
|
},
|
|
{
|
|
in: `-histogram_quantile(0.9,rate(foo[1m]))`,
|
|
out: `-histogram_quantile(
|
|
0.9,
|
|
rate(
|
|
foo[1m]
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `-histogram_quantile(0.99, sum by (le) (rate(foo[1m])))`,
|
|
out: `-histogram_quantile(
|
|
0.99,
|
|
sum by(le) (
|
|
rate(
|
|
foo[1m]
|
|
)
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `-histogram_quantile(0.9, -rate(foo[1m] @ start()))`,
|
|
out: `-histogram_quantile(
|
|
0.9,
|
|
-rate(
|
|
foo[1m] @ start()
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(-histogram_quantile(0.9, -rate(foo[1m] @ start())))`,
|
|
out: `(
|
|
-histogram_quantile(
|
|
0.9,
|
|
-rate(
|
|
foo[1m] @ start()
|
|
)
|
|
)
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|