mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-03-21 15:45:01 +00:00
app/vmselect/prometheus: properly encode Prometheus label values at /federate endpoint
Prometheus spec says that only \, \n and " must be escaped inside label values.
See 995743836e/content/docs/instrumenting/exposition_formats.md (L90)
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5431
This commit is contained in:
parent
6a45349759
commit
2d1cd538cb
5 changed files with 180 additions and 5 deletions
|
@ -149,11 +149,11 @@
|
||||||
{% if len(mn.Tags) > 0 %}
|
{% if len(mn.Tags) > 0 %}
|
||||||
{
|
{
|
||||||
{% code tags := mn.Tags %}
|
{% code tags := mn.Tags %}
|
||||||
{%z= tags[0].Key %}={%qz= tags[0].Value %}
|
{%z= tags[0].Key %}={%= escapePrometheusLabel(tags[0].Value) %}
|
||||||
{% code tags = tags[1:] %}
|
{% code tags = tags[1:] %}
|
||||||
{% for i := range tags %}
|
{% for i := range tags %}
|
||||||
{% code tag := &tags[i] %}
|
{% code tag := &tags[i] %}
|
||||||
,{%z= tag.Key %}={%qz= tag.Value %}
|
,{%z= tag.Key %}={%= escapePrometheusLabel(tag.Value) %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -173,4 +173,26 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func escapePrometheusLabel(b []byte) %}
|
||||||
|
"
|
||||||
|
{% for len(b) > 0 %}
|
||||||
|
{% code n := bytes.IndexAny(b, "\\\n\"") %}
|
||||||
|
{% if n < 0 %}
|
||||||
|
{%z= b %}
|
||||||
|
{% break %}
|
||||||
|
{% endif %}
|
||||||
|
{%z= b[:n] %}
|
||||||
|
{% switch b[n] %}
|
||||||
|
{% case '\\' %}
|
||||||
|
\\
|
||||||
|
{% case '\n' %}
|
||||||
|
\n
|
||||||
|
{% case '"' %}
|
||||||
|
\"
|
||||||
|
{% endswitch %}
|
||||||
|
{% code b = b[n+1:] %}
|
||||||
|
{% endfor %}
|
||||||
|
"
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
{% endstripspace %}
|
{% endstripspace %}
|
||||||
|
|
|
@ -495,7 +495,7 @@ func streamprometheusMetricName(qw422016 *qt422016.Writer, mn *storage.MetricNam
|
||||||
//line app/vmselect/prometheus/export.qtpl:152
|
//line app/vmselect/prometheus/export.qtpl:152
|
||||||
qw422016.N().S(`=`)
|
qw422016.N().S(`=`)
|
||||||
//line app/vmselect/prometheus/export.qtpl:152
|
//line app/vmselect/prometheus/export.qtpl:152
|
||||||
qw422016.N().QZ(tags[0].Value)
|
streamescapePrometheusLabel(qw422016, tags[0].Value)
|
||||||
//line app/vmselect/prometheus/export.qtpl:153
|
//line app/vmselect/prometheus/export.qtpl:153
|
||||||
tags = tags[1:]
|
tags = tags[1:]
|
||||||
|
|
||||||
|
@ -511,7 +511,7 @@ func streamprometheusMetricName(qw422016 *qt422016.Writer, mn *storage.MetricNam
|
||||||
//line app/vmselect/prometheus/export.qtpl:156
|
//line app/vmselect/prometheus/export.qtpl:156
|
||||||
qw422016.N().S(`=`)
|
qw422016.N().S(`=`)
|
||||||
//line app/vmselect/prometheus/export.qtpl:156
|
//line app/vmselect/prometheus/export.qtpl:156
|
||||||
qw422016.N().QZ(tag.Value)
|
streamescapePrometheusLabel(qw422016, tag.Value)
|
||||||
//line app/vmselect/prometheus/export.qtpl:157
|
//line app/vmselect/prometheus/export.qtpl:157
|
||||||
}
|
}
|
||||||
//line app/vmselect/prometheus/export.qtpl:157
|
//line app/vmselect/prometheus/export.qtpl:157
|
||||||
|
@ -599,3 +599,74 @@ func convertValueToSpecialJSON(v float64) string {
|
||||||
return qs422016
|
return qs422016
|
||||||
//line app/vmselect/prometheus/export.qtpl:174
|
//line app/vmselect/prometheus/export.qtpl:174
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:176
|
||||||
|
func streamescapePrometheusLabel(qw422016 *qt422016.Writer, b []byte) {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:176
|
||||||
|
qw422016.N().S(`"`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:178
|
||||||
|
for len(b) > 0 {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:179
|
||||||
|
n := bytes.IndexAny(b, "\\\n\"")
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:180
|
||||||
|
if n < 0 {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:181
|
||||||
|
qw422016.N().Z(b)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:182
|
||||||
|
break
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:183
|
||||||
|
}
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:184
|
||||||
|
qw422016.N().Z(b[:n])
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:185
|
||||||
|
switch b[n] {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:186
|
||||||
|
case '\\':
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:186
|
||||||
|
qw422016.N().S(`\\`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:188
|
||||||
|
case '\n':
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:188
|
||||||
|
qw422016.N().S(`\n`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:190
|
||||||
|
case '"':
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:190
|
||||||
|
qw422016.N().S(`\"`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:192
|
||||||
|
}
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:193
|
||||||
|
b = b[n+1:]
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:194
|
||||||
|
}
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:194
|
||||||
|
qw422016.N().S(`"`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
}
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
func writeescapePrometheusLabel(qq422016 qtio422016.Writer, b []byte) {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
streamescapePrometheusLabel(qw422016, b)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
}
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
func escapePrometheusLabel(b []byte) string {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
writeescapePrometheusLabel(qb422016, b)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
return qs422016
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
}
|
||||||
|
|
43
app/vmselect/prometheus/federate_test.go
Normal file
43
app/vmselect/prometheus/federate_test.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFederate(t *testing.T) {
|
||||||
|
f := func(rs *netstorage.Result, expectedResult string) {
|
||||||
|
t.Helper()
|
||||||
|
result := Federate(rs)
|
||||||
|
if result != expectedResult {
|
||||||
|
t.Fatalf("unexpected result; got\n%s\nwant\n%s", result, expectedResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(&netstorage.Result{}, ``)
|
||||||
|
|
||||||
|
f(&netstorage.Result{
|
||||||
|
MetricName: storage.MetricName{
|
||||||
|
MetricGroup: []byte("foo"),
|
||||||
|
Tags: []storage.Tag{
|
||||||
|
{
|
||||||
|
Key: []byte("a"),
|
||||||
|
Value: []byte("b"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: []byte("qqq"),
|
||||||
|
Value: []byte("\\"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: []byte("abc"),
|
||||||
|
// Verify that < isn't encoded. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5431
|
||||||
|
Value: []byte("a<b\"\\c"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Values: []float64{1.23},
|
||||||
|
Timestamps: []int64{123},
|
||||||
|
}, `foo{a="b",qqq="\\",abc="a<b\"\\c"} 1.23 123`+"\n")
|
||||||
|
}
|
38
app/vmselect/prometheus/federate_timing_test.go
Normal file
38
app/vmselect/prometheus/federate_timing_test.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkFederate(b *testing.B) {
|
||||||
|
rs := &netstorage.Result{
|
||||||
|
MetricName: storage.MetricName{
|
||||||
|
MetricGroup: []byte("foo_bar_bazaaaa_total"),
|
||||||
|
Tags: []storage.Tag{
|
||||||
|
{
|
||||||
|
Key: []byte("instance"),
|
||||||
|
Value: []byte("foobarbaz:2344"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: []byte("job"),
|
||||||
|
Value: []byte("aaabbbccc"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Values: []float64{112.23},
|
||||||
|
Timestamps: []int64{1234567890},
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
for pb.Next() {
|
||||||
|
bb.Reset()
|
||||||
|
WriteFederate(&bb, rs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -16,7 +16,8 @@ The following `tip` changes can be tested by building VictoriaMetrics components
|
||||||
|
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): prevent from `FATAL: cannot flush metainfo` panic when [`-remoteWrite.multitenantURL`](https://docs.victoriametrics.com/vmagent.html#multitenancy) command-line flag is set. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5357).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): prevent from `FATAL: cannot flush metainfo` panic when [`-remoteWrite.multitenantURL`](https://docs.victoriametrics.com/vmagent.html#multitenancy) command-line flag is set. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5357).
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly decode zstd-encoded data blocks received via [VictoriaMetrics remote_write protocol](https://docs.victoriametrics.com/vmagent.html#victoriametrics-remote-write-protocol). See [this issue comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5301#issuecomment-1815871992).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly decode zstd-encoded data blocks received via [VictoriaMetrics remote_write protocol](https://docs.victoriametrics.com/vmagent.html#victoriametrics-remote-write-protocol). See [this issue comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5301#issuecomment-1815871992).
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly add new labels at `output_relabel_configs` during [stream aggregation](https://docs.victoriametrics.com/stream-aggregation.html). Previously this could lead to corrupted labels in output samples. Thanks to @ChengChung for providing [detailed report for this bug](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5402).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly add new labels at `output_relabel_configs` during [stream aggregation](https://docs.victoriametrics.com/stream-aggregation.html). Previously this could lead to corrupted labels in output samples. Thanks to @ChengChung for providing [detailed report for this bug](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5402).
|
||||||
|
* BUGFIX: properly escape `<` character in responses returned via [`/federate`](https://docs.victoriametrics.com/#federation) endpoint. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5431).
|
||||||
|
|
||||||
## [v1.93.8](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.93.8)
|
## [v1.93.8](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.93.8)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue