mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-14 16:12:15 +01:00
app/vmselect: add /select/0/prometheus/expand-with-exprs page
This commit is contained in:
parent
dafa819c54
commit
6d13ca2b88
@ -305,7 +305,7 @@ func selectHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseW
|
||||
fmt.Fprintf(w, "See <a href='https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html#url-format'>docs</a></br>")
|
||||
fmt.Fprintf(w, "Useful endpoints:</br>")
|
||||
fmt.Fprintf(w, `<a href="vmui">Web UI</a><br>`)
|
||||
fmt.Fprintf(w, `<a href="metric-relabel-debug">Metric-level relabel debugging</a></br>`)
|
||||
fmt.Fprintf(w, `<a href="prometheus/metric-relabel-debug">Metric-level relabel debugging</a></br>`)
|
||||
fmt.Fprintf(w, `<a href="prometheus/api/v1/status/tsdb">tsdb status page</a><br>`)
|
||||
fmt.Fprintf(w, `<a href="prometheus/api/v1/status/top_queries">top queries</a><br>`)
|
||||
fmt.Fprintf(w, `<a href="prometheus/api/v1/status/active_queries">active queries</a><br>`)
|
||||
@ -594,6 +594,10 @@ func selectHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseW
|
||||
promscrapeTargetRelabelDebugRequests.Inc()
|
||||
promscrape.WriteTargetRelabelDebug(w, r)
|
||||
return true
|
||||
case "prometheus/expand-with-exprs", "expand-with-exprs":
|
||||
expandWithExprsRequests.Inc()
|
||||
prometheus.ExpandWithExprs(w, r)
|
||||
return true
|
||||
case "prometheus/api/v1/rules", "prometheus/rules":
|
||||
rulesRequests.Inc()
|
||||
if len(*vmalertProxyURL) > 0 {
|
||||
@ -768,6 +772,8 @@ var (
|
||||
promscrapeMetricRelabelDebugRequests = metrics.NewCounter(`vm_http_requests_total{path="/select/{}/prometheus/metric-relabel-debug"}`)
|
||||
promscrapeTargetRelabelDebugRequests = metrics.NewCounter(`vm_http_requests_total{path="/select/{}/prometheus/target-relabel-debug"}`)
|
||||
|
||||
expandWithExprsRequests = metrics.NewCounter(`vm_http_requests_total{path="/select/{}/prometheus/expand-with-exprs"}`)
|
||||
|
||||
vmalertRequests = metrics.NewCounter(`vm_http_requests_total{path="/select/{}/prometheus/vmalert"}`)
|
||||
rulesRequests = metrics.NewCounter(`vm_http_requests_total{path="/select/{}/prometheus/api/v1/rules"}`)
|
||||
alertsRequests = metrics.NewCounter(`vm_http_requests_total{path="/select/{}/prometheus/api/v1/alerts"}`)
|
||||
|
245
app/vmselect/prometheus/expand-with-exprs.qtpl
Normal file
245
app/vmselect/prometheus/expand-with-exprs.qtpl
Normal file
@ -0,0 +1,245 @@
|
||||
{% import (
|
||||
"github.com/VictoriaMetrics/metricsql"
|
||||
) %}
|
||||
|
||||
{% stripspace %}
|
||||
|
||||
// ExpandWithExprsResponse returns a webpage, which expands with templates in q MetricsQL.
|
||||
{% func ExpandWithExprsResponse(q string) %}
|
||||
<html>
|
||||
<head>
|
||||
<title>Expand WITH expressions</title>
|
||||
<style>
|
||||
p { font-weight: bold }
|
||||
textarea { margin: 1em }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<form method="get">
|
||||
<div>
|
||||
<p>
|
||||
<a href="https://docs.victoriametrics.com/MetricsQL.html">MetricsQL</a> query with optional WITH expressions:
|
||||
</p>
|
||||
<textarea name="query" style="height: 15em; width: 90%">{%s q %}</textarea><br/>
|
||||
<input type="submit" value="Expand" />
|
||||
|
||||
<p>
|
||||
<a href="https://docs.victoriametrics.com/MetricsQL.html">MetricsQL</a> query after expanding WITH expressions and applying other optimizations:
|
||||
</p>
|
||||
<textarea style="height: 5em; width: 90%" readonly="readonly">{%= expandWithExprs(q) %}</textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>{%= withExprsTutorial() %}</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% endfunc %}
|
||||
|
||||
{% func expandWithExprs(q string) %}
|
||||
{% if len(q) == 0 %}
|
||||
{% return %}
|
||||
{% endif %}
|
||||
|
||||
{% code expr, err := metricsql.Parse(q) %}
|
||||
{% if err != nil %}
|
||||
Cannot parse query: {%v err %}
|
||||
{% else %}
|
||||
{% code expr = metricsql.Optimize(expr) %}
|
||||
{%z expr.AppendString(nil) %}
|
||||
{% endif %}
|
||||
{% endfunc %}
|
||||
|
||||
{% endstripspace %}
|
||||
|
||||
{% func withExprsTutorial() %}
|
||||
<h3>Tutorial for WITH expressions in <a href="https://docs.victoriametrics.com/MetricsQL.html">MetricsQL</a></h3>
|
||||
|
||||
<p>
|
||||
Let's look at the following real query from <a href="https://grafana.com/dashboards/1860">Node Exporter Full</a> dashboard:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(
|
||||
(
|
||||
node_memory_MemTotal_bytes{instance=~"$node:$port", job=~"$job"}
|
||||
-
|
||||
node_memory_MemFree_bytes{instance=~"$node:$port", job=~"$job"}
|
||||
)
|
||||
/
|
||||
node_memory_MemTotal_bytes{instance=~"$node:$port", job=~"$job"}
|
||||
)
|
||||
*
|
||||
100
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
It is clear the query calculates the percentage of used memory
|
||||
for the given $node, $port and $job. Isn't it? :)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
What's wrong with this query? Copy-pasted label filters for distinct timeseries
|
||||
which makes it easy to mistype these filters during modification.
|
||||
Let's simplify the query with WITH expressions:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"}
|
||||
)
|
||||
(
|
||||
node_memory_MemTotal_bytes{commonFilters}
|
||||
-
|
||||
node_memory_MemFree_bytes{commonFilters}
|
||||
)
|
||||
/
|
||||
node_memory_MemTotal_bytes{commonFilters} * 100
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now label filters are located in a single place instead of three distinct places.
|
||||
The query mentions node_memory_MemTotal_bytes metric twice and {commonFilters}
|
||||
three times. WITH expressions may improve this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
my_resource_utilization(free, limit, filters) = (limit{filters} - free{filters}) / limit{filters} * 100
|
||||
)
|
||||
my_resource_utilization(
|
||||
node_memory_MemFree_bytes,
|
||||
node_memory_MemTotal_bytes,
|
||||
{instance=~"$node:$port",job=~"$job"},
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now the template function my_resource_utilization() may be used for monitoring arbitrary
|
||||
resources - memory, CPU, network, storage, you name it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Let's take another nice query from <a href="https://grafana.com/dashboards/1860">Node Exporter Full</a> dashboard:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(
|
||||
(
|
||||
(
|
||||
count(
|
||||
count(node_cpu_seconds_total{instance=~"$node:$port",job=~"$job"}) by (cpu)
|
||||
)
|
||||
)
|
||||
-
|
||||
avg(
|
||||
sum by (mode) (rate(node_cpu_seconds_total{mode='idle',instance=~"$node:$port",job=~"$job"}[5m]))
|
||||
)
|
||||
)
|
||||
*
|
||||
100
|
||||
)
|
||||
/
|
||||
count(
|
||||
count(node_cpu_seconds_total{instance=~"$node:$port",job=~"$job"}) by (cpu)
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Do you understand what does this mess do? Is it manageable? :) WITH expressions are happy to help in a few iterations.
|
||||
<br/>
|
||||
<br/>
|
||||
1. Extract common filters used in multiple places into a commonFilters variable:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"}
|
||||
)
|
||||
(
|
||||
(
|
||||
(
|
||||
count(
|
||||
count(node_cpu_seconds_total{commonFilters}) by (cpu)
|
||||
)
|
||||
)
|
||||
-
|
||||
avg(
|
||||
sum by (mode) (rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))
|
||||
)
|
||||
)
|
||||
*
|
||||
100
|
||||
)
|
||||
/
|
||||
count(
|
||||
count(node_cpu_seconds_total{commonFilters}) by (cpu)
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
2. Extract "count(count(...) by (cpu))" into cpuCount variable:
|
||||
</p>
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"},
|
||||
cpuCount = count(count(node_cpu_seconds_total{commonFilters}) by (cpu))
|
||||
)
|
||||
(
|
||||
(
|
||||
cpuCount
|
||||
-
|
||||
avg(
|
||||
sum by (mode) (rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))
|
||||
)
|
||||
)
|
||||
*
|
||||
100
|
||||
) / cpuCount
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
3. Extract rate(...) part into cpuIdle variable, since it is clear now that this part calculates the number of idle CPUs:
|
||||
</p>
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"},
|
||||
cpuCount = count(count(node_cpu_seconds_total{commonFilters}) by (cpu)),
|
||||
cpuIdle = sum(rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))
|
||||
)
|
||||
((cpuCount - cpuIdle) * 100) / cpuCount
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
4. Put node_cpu_seconds_total{commonFilters} into its own varialbe with the name cpuSeconds:
|
||||
</p>
|
||||
<pre>
|
||||
WITH (
|
||||
cpuSeconds = node_cpu_seconds_total{instance=~"$node:$port",job=~"$job"},
|
||||
cpuCount = count(count(cpuSeconds) by (cpu)),
|
||||
cpuIdle = sum(rate(cpuSeconds{mode='idle'}[5m]))
|
||||
)
|
||||
((cpuCount - cpuIdle) * 100) / cpuCount
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now the query became more clear comparing to the initial query.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
WITH expressions may be nested and may be put anywhere. Try expanding the following query:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
f(a, b) = WITH (
|
||||
f1(x) = b-x,
|
||||
f2(x) = x+x
|
||||
) f1(a)*f2(b)
|
||||
) f(foo, with(x=bar) x)
|
||||
</pre>
|
||||
|
||||
{% endfunc %}
|
345
app/vmselect/prometheus/expand-with-exprs.qtpl.go
Normal file
345
app/vmselect/prometheus/expand-with-exprs.qtpl.go
Normal file
@ -0,0 +1,345 @@
|
||||
// Code generated by qtc from "expand-with-exprs.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:1
|
||||
package prometheus
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:1
|
||||
import (
|
||||
"github.com/VictoriaMetrics/metricsql"
|
||||
)
|
||||
|
||||
// ExpandWithExprsResponse returns a webpage, which expands with templates in q MetricsQL.
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:8
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:8
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:8
|
||||
func StreamExpandWithExprsResponse(qw422016 *qt422016.Writer, q string) {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:8
|
||||
qw422016.N().S(`<html><head><title>Expand WITH expressions</title><style>p { font-weight: bold }textarea { margin: 1em }</style></head><body><div><form method="get"><div><p><a href="https://docs.victoriametrics.com/MetricsQL.html">MetricsQL</a> query with optional WITH expressions:</p><textarea name="query" style="height: 15em; width: 90%">`)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:25
|
||||
qw422016.E().S(q)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:25
|
||||
qw422016.N().S(`</textarea><br/><input type="submit" value="Expand" /><p><a href="https://docs.victoriametrics.com/MetricsQL.html">MetricsQL</a> query after expanding WITH expressions and applying other optimizations:</p><textarea style="height: 5em; width: 90%" readonly="readonly">`)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:31
|
||||
streamexpandWithExprs(qw422016, q)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:31
|
||||
qw422016.N().S(`</textarea></div></form></div><div>`)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:36
|
||||
streamwithExprsTutorial(qw422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:36
|
||||
qw422016.N().S(`</div></body></html>`)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
func WriteExpandWithExprsResponse(qq422016 qtio422016.Writer, q string) {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
StreamExpandWithExprsResponse(qw422016, q)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
func ExpandWithExprsResponse(q string) string {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
WriteExpandWithExprsResponse(qb422016, q)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
return qs422016
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:40
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:42
|
||||
func streamexpandWithExprs(qw422016 *qt422016.Writer, q string) {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:43
|
||||
if len(q) == 0 {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:44
|
||||
return
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:45
|
||||
}
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:47
|
||||
expr, err := metricsql.Parse(q)
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:48
|
||||
if err != nil {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:48
|
||||
qw422016.N().S(`Cannot parse query:`)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:49
|
||||
qw422016.E().V(err)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:50
|
||||
} else {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:51
|
||||
expr = metricsql.Optimize(expr)
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:52
|
||||
qw422016.E().Z(expr.AppendString(nil))
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:53
|
||||
}
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
func writeexpandWithExprs(qq422016 qtio422016.Writer, q string) {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
streamexpandWithExprs(qw422016, q)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
func expandWithExprs(q string) string {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
writeexpandWithExprs(qb422016, q)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
return qs422016
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:54
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:58
|
||||
func streamwithExprsTutorial(qw422016 *qt422016.Writer) {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:58
|
||||
qw422016.N().S(`
|
||||
<h3>Tutorial for WITH expressions in <a href="https://docs.victoriametrics.com/MetricsQL.html">MetricsQL</a></h3>
|
||||
|
||||
<p>
|
||||
Let's look at the following real query from <a href="https://grafana.com/dashboards/1860">Node Exporter Full</a> dashboard:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(
|
||||
(
|
||||
node_memory_MemTotal_bytes{instance=~"$node:$port", job=~"$job"}
|
||||
-
|
||||
node_memory_MemFree_bytes{instance=~"$node:$port", job=~"$job"}
|
||||
)
|
||||
/
|
||||
node_memory_MemTotal_bytes{instance=~"$node:$port", job=~"$job"}
|
||||
)
|
||||
*
|
||||
100
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
It is clear the query calculates the percentage of used memory
|
||||
for the given $node, $port and $job. Isn't it? :)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
What's wrong with this query? Copy-pasted label filters for distinct timeseries
|
||||
which makes it easy to mistype these filters during modification.
|
||||
Let's simplify the query with WITH expressions:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"}
|
||||
)
|
||||
(
|
||||
node_memory_MemTotal_bytes{commonFilters}
|
||||
-
|
||||
node_memory_MemFree_bytes{commonFilters}
|
||||
)
|
||||
/
|
||||
node_memory_MemTotal_bytes{commonFilters} * 100
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now label filters are located in a single place instead of three distinct places.
|
||||
The query mentions node_memory_MemTotal_bytes metric twice and {commonFilters}
|
||||
three times. WITH expressions may improve this:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
my_resource_utilization(free, limit, filters) = (limit{filters} - free{filters}) / limit{filters} * 100
|
||||
)
|
||||
my_resource_utilization(
|
||||
node_memory_MemFree_bytes,
|
||||
node_memory_MemTotal_bytes,
|
||||
{instance=~"$node:$port",job=~"$job"},
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now the template function my_resource_utilization() may be used for monitoring arbitrary
|
||||
resources - memory, CPU, network, storage, you name it.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Let's take another nice query from <a href="https://grafana.com/dashboards/1860">Node Exporter Full</a> dashboard:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
(
|
||||
(
|
||||
(
|
||||
count(
|
||||
count(node_cpu_seconds_total{instance=~"$node:$port",job=~"$job"}) by (cpu)
|
||||
)
|
||||
)
|
||||
-
|
||||
avg(
|
||||
sum by (mode) (rate(node_cpu_seconds_total{mode='idle',instance=~"$node:$port",job=~"$job"}[5m]))
|
||||
)
|
||||
)
|
||||
*
|
||||
100
|
||||
)
|
||||
/
|
||||
count(
|
||||
count(node_cpu_seconds_total{instance=~"$node:$port",job=~"$job"}) by (cpu)
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Do you understand what does this mess do? Is it manageable? :) WITH expressions are happy to help in a few iterations.
|
||||
<br/>
|
||||
<br/>
|
||||
1. Extract common filters used in multiple places into a commonFilters variable:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"}
|
||||
)
|
||||
(
|
||||
(
|
||||
(
|
||||
count(
|
||||
count(node_cpu_seconds_total{commonFilters}) by (cpu)
|
||||
)
|
||||
)
|
||||
-
|
||||
avg(
|
||||
sum by (mode) (rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))
|
||||
)
|
||||
)
|
||||
*
|
||||
100
|
||||
)
|
||||
/
|
||||
count(
|
||||
count(node_cpu_seconds_total{commonFilters}) by (cpu)
|
||||
)
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
2. Extract "count(count(...) by (cpu))" into cpuCount variable:
|
||||
</p>
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"},
|
||||
cpuCount = count(count(node_cpu_seconds_total{commonFilters}) by (cpu))
|
||||
)
|
||||
(
|
||||
(
|
||||
cpuCount
|
||||
-
|
||||
avg(
|
||||
sum by (mode) (rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))
|
||||
)
|
||||
)
|
||||
*
|
||||
100
|
||||
) / cpuCount
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
3. Extract rate(...) part into cpuIdle variable, since it is clear now that this part calculates the number of idle CPUs:
|
||||
</p>
|
||||
<pre>
|
||||
WITH (
|
||||
commonFilters = {instance=~"$node:$port",job=~"$job"},
|
||||
cpuCount = count(count(node_cpu_seconds_total{commonFilters}) by (cpu)),
|
||||
cpuIdle = sum(rate(node_cpu_seconds_total{mode='idle',commonFilters}[5m]))
|
||||
)
|
||||
((cpuCount - cpuIdle) * 100) / cpuCount
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
4. Put node_cpu_seconds_total{commonFilters} into its own varialbe with the name cpuSeconds:
|
||||
</p>
|
||||
<pre>
|
||||
WITH (
|
||||
cpuSeconds = node_cpu_seconds_total{instance=~"$node:$port",job=~"$job"},
|
||||
cpuCount = count(count(cpuSeconds) by (cpu)),
|
||||
cpuIdle = sum(rate(cpuSeconds{mode='idle'}[5m]))
|
||||
)
|
||||
((cpuCount - cpuIdle) * 100) / cpuCount
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Now the query became more clear comparing to the initial query.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
WITH expressions may be nested and may be put anywhere. Try expanding the following query:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
WITH (
|
||||
f(a, b) = WITH (
|
||||
f1(x) = b-x,
|
||||
f2(x) = x+x
|
||||
) f1(a)*f2(b)
|
||||
) f(foo, with(x=bar) x)
|
||||
</pre>
|
||||
|
||||
`)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
func writewithExprsTutorial(qq422016 qtio422016.Writer) {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
streamwithExprsTutorial(qw422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
}
|
||||
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
func withExprsTutorial() string {
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
writewithExprsTutorial(qb422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
return qs422016
|
||||
//line app/vmselect/prometheus/expand-with-exprs.qtpl:245
|
||||
}
|
@ -61,6 +61,15 @@ var (
|
||||
// Default step used if not set.
|
||||
const defaultStep = 5 * 60 * 1000
|
||||
|
||||
// ExpandWithExprs handles the request to /expand-with-exprs
|
||||
func ExpandWithExprs(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.FormValue("query")
|
||||
bw := bufferedwriter.Get(w)
|
||||
defer bufferedwriter.Put(bw)
|
||||
WriteExpandWithExprsResponse(bw, query)
|
||||
_ = bw.Flush()
|
||||
}
|
||||
|
||||
// FederateHandler implements /federate . See https://prometheus.io/docs/prometheus/latest/federation/
|
||||
func FederateHandler(startTime time.Time, at *auth.Token, w http.ResponseWriter, r *http.Request) error {
|
||||
defer federateDuration.UpdateDuration(startTime)
|
||||
@ -413,10 +422,7 @@ func exportHandler(qt *querytracer.Tracer, at *auth.Token, w http.ResponseWriter
|
||||
if format == "promapi" {
|
||||
WriteExportPromAPIFooter(bw, qt)
|
||||
}
|
||||
if err := bw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return bw.Flush()
|
||||
}
|
||||
|
||||
type exportBlock struct {
|
||||
|
@ -673,7 +673,7 @@ Released at 12-04-2022
|
||||
* BUGFIX: [VictoriaMetrics cluster](https://docs.victoriametrics.com/Cluster-VictoriaMetrics.html): properly propagate limits at `-search.max*` command-line flags from `vminsert` to `vmstorage`. The limits are `-search.maxUniqueTimeseries`, `-search.maxSeries`, `-search.maxFederateSeries`, `-search.maxExportSeries`, `-search.maxGraphiteSeries` and `-search.maxTSDBStatusSeries`. They weren't propagated to `vmstorage` because of the bug. These limits were introduced in [v1.76.0](https://docs.victoriametrics.com/CHANGELOG.html#v1760). See [this bug](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2450).
|
||||
* BUGFIX: fix goroutine leak and possible deadlock when importing invalid data via [native binary format](https://docs.victoriametrics.com/#how-to-import-data-in-native-format). See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2423).
|
||||
* BUGFIX: [Graphite Render API](https://docs.victoriametrics.com/#graphite-render-api-usage): properly calculate [hitCount](https://graphite.readthedocs.io/en/latest/functions.html#graphite.render.functions.hitcount) function. Previously it could return empty results if there were no original samples in some parts of the selected time range.
|
||||
* BUGFIX: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): allow overriding built-in function names inside [WITH templates](https://play.victoriametrics.com/promql/expand-with-exprs). For example, `WITH (sum(a,b) = a + b + 1) sum(x,y)` now expands into `x + y + 1`. Previously such a query would fail with `cannot use reserved name` error. See [this bugreport](https://github.com/VictoriaMetrics/metricsql/issues/5).
|
||||
* BUGFIX: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): allow overriding built-in function names inside [WITH templates](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/expand-with-exprs). For example, `WITH (sum(a,b) = a + b + 1) sum(x,y)` now expands into `x + y + 1`. Previously such a query would fail with `cannot use reserved name` error. See [this bugreport](https://github.com/VictoriaMetrics/metricsql/issues/5).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): properly display values greater than 1000 on Y axis. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2409).
|
||||
|
||||
|
||||
|
@ -90,7 +90,7 @@ The list of MetricsQL features:
|
||||
* `if` binary operator. `q1 if q2` removes values from `q1` for missing values from `q2`.
|
||||
* `ifnot` binary operator. `q1 ifnot q2` removes values from `q1` for existing values from `q2`.
|
||||
* `WITH` templates. This feature simplifies writing and managing complex queries.
|
||||
Go to [WITH templates playground](https://play.victoriametrics.com/promql/expand-with-exprs) and try it.
|
||||
Go to [WITH templates playground](https://play.victoriametrics.com/select/accounting/1/6a716b0f-38bc-4856-90ce-448fd713e3fe/expand-with-exprs) and try it.
|
||||
* String literals may be concatenated. This is useful with `WITH` templates:
|
||||
`WITH (commonPrefix="long_metric_prefix_") {__name__=commonPrefix+"suffix1"} / {__name__=commonPrefix+"suffix2"}`.
|
||||
* `keep_metric_names` modifier can be applied to all the [rollup functions](#rollup-functions) and [transform functions](#transform-functions).
|
||||
|
Loading…
Reference in New Issue
Block a user