Make Prometheus understand the new text format v0.0.4.
Change-Id: I42b834528c9c75d3d97443612bb05ce198ba4dc4
This commit is contained in:
parent
46fc7a3748
commit
ad3452a46c
|
@ -40,7 +40,16 @@ func ProcessorForRequestHeader(header http.Header) (Processor, error) {
|
||||||
return nil, fmt.Errorf("Unsupported Encoding %s", params["encoding"])
|
return nil, fmt.Errorf("Unsupported Encoding %s", params["encoding"])
|
||||||
}
|
}
|
||||||
return MetricFamilyProcessor, nil
|
return MetricFamilyProcessor, nil
|
||||||
|
case "text/plain":
|
||||||
|
switch params["version"] {
|
||||||
|
case "0.0.4":
|
||||||
|
return Processor004, nil
|
||||||
|
case "":
|
||||||
|
// Fallback: most recent version.
|
||||||
|
return Processor004, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unrecognized API version %s", params["version"])
|
||||||
|
}
|
||||||
case "application/json":
|
case "application/json":
|
||||||
var prometheusApiVersion string
|
var prometheusApiVersion string
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,21 @@ func testDiscriminatorHttpHeader(t test.Tester) {
|
||||||
output: nil,
|
output: nil,
|
||||||
err: fmt.Errorf("Unsupported Encoding illegal"),
|
err: fmt.Errorf("Unsupported Encoding illegal"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: map[string]string{"Content-Type": `text/plain; version=0.0.4`},
|
||||||
|
output: Processor004,
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: map[string]string{"Content-Type": `text/plain`},
|
||||||
|
output: Processor004,
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: map[string]string{"Content-Type": `text/plain; version=0.0.3`},
|
||||||
|
output: nil,
|
||||||
|
err: fmt.Errorf("Unrecognized API version 0.0.3"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, scenario := range scenarios {
|
for i, scenario := range scenarios {
|
||||||
|
|
|
@ -31,10 +31,10 @@ type metricFamilyProcessor struct{}
|
||||||
//
|
//
|
||||||
// See http://godoc.org/github.com/matttproud/golang_protobuf_extensions/ext for
|
// See http://godoc.org/github.com/matttproud/golang_protobuf_extensions/ext for
|
||||||
// more details.
|
// more details.
|
||||||
var MetricFamilyProcessor = new(metricFamilyProcessor)
|
var MetricFamilyProcessor = &metricFamilyProcessor{}
|
||||||
|
|
||||||
func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *ProcessOptions) error {
|
func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *ProcessOptions) error {
|
||||||
family := new(dto.MetricFamily)
|
family := &dto.MetricFamily{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
family.Reset()
|
family.Reset()
|
||||||
|
@ -43,10 +43,15 @@ func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *Proc
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := extractMetricFamily(out, o, family); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractMetricFamily(out Ingester, o *ProcessOptions, family *dto.MetricFamily) error {
|
||||||
switch *family.Type {
|
switch *family.Type {
|
||||||
case dto.MetricType_COUNTER:
|
case dto.MetricType_COUNTER:
|
||||||
if err := extractCounter(out, o, family); err != nil {
|
if err := extractCounter(out, o, family); err != nil {
|
||||||
|
@ -65,8 +70,6 @@ func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *Proc
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@ import (
|
||||||
// ProcessOptions dictates how the interpreted stream should be rendered for
|
// ProcessOptions dictates how the interpreted stream should be rendered for
|
||||||
// consumption.
|
// consumption.
|
||||||
type ProcessOptions struct {
|
type ProcessOptions struct {
|
||||||
// Timestamp is added to each value interpreted from the stream.
|
// Timestamp is added to each value from the stream that has no explicit
|
||||||
|
// timestamp set.
|
||||||
Timestamp model.Timestamp
|
Timestamp model.Timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2014 Prometheus Team
|
||||||
|
// 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 extraction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/text"
|
||||||
|
)
|
||||||
|
|
||||||
|
type processor004 struct{}
|
||||||
|
|
||||||
|
// Processor004 s responsible for decoding payloads from the text based variety
|
||||||
|
// of protocol version 0.0.4.
|
||||||
|
var Processor004 = &processor004{}
|
||||||
|
|
||||||
|
func (t *processor004) ProcessSingle(i io.Reader, out Ingester, o *ProcessOptions) error {
|
||||||
|
var parser text.Parser
|
||||||
|
metricFamilies, err := parser.TextToMetricFamilies(i)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, metricFamily := range metricFamilies {
|
||||||
|
if err := extractMetricFamily(out, o, metricFamily); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright 2014 Prometheus Team
|
||||||
|
// 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 extraction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ts = model.Now()
|
||||||
|
in = `
|
||||||
|
# Only a quite simple scenario with two metric families.
|
||||||
|
# More complicated tests of the parser itself can be found in the text package.
|
||||||
|
# TYPE mf2 counter
|
||||||
|
mf2 3
|
||||||
|
mf1{label="value1"} -3.14 123456
|
||||||
|
mf1{label="value2"} 42
|
||||||
|
mf2 4
|
||||||
|
`
|
||||||
|
out = map[model.LabelValue]*Result{
|
||||||
|
"mf1": {
|
||||||
|
Samples: model.Samples{
|
||||||
|
&model.Sample{
|
||||||
|
Metric: model.Metric{model.MetricNameLabel: "mf1", "label": "value1"},
|
||||||
|
Value: -3.14,
|
||||||
|
Timestamp: 123,
|
||||||
|
},
|
||||||
|
&model.Sample{
|
||||||
|
Metric: model.Metric{model.MetricNameLabel: "mf1", "label": "value2"},
|
||||||
|
Value: 42,
|
||||||
|
Timestamp: ts,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"mf2": {
|
||||||
|
Samples: model.Samples{
|
||||||
|
&model.Sample{
|
||||||
|
Metric: model.Metric{model.MetricNameLabel: "mf2"},
|
||||||
|
Value: 3,
|
||||||
|
Timestamp: ts,
|
||||||
|
},
|
||||||
|
&model.Sample{
|
||||||
|
Metric: model.Metric{model.MetricNameLabel: "mf2"},
|
||||||
|
Value: 4,
|
||||||
|
Timestamp: ts,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type testIngester struct {
|
||||||
|
results []*Result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *testIngester) Ingest(r *Result) error {
|
||||||
|
i.results = append(i.results, r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTextProcessor(t *testing.T) {
|
||||||
|
var ingester testIngester
|
||||||
|
i := strings.NewReader(in)
|
||||||
|
o := &ProcessOptions{
|
||||||
|
Timestamp: ts,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Processor004.ProcessSingle(i, &ingester, o)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if expected, got := len(out), len(ingester.results); expected != got {
|
||||||
|
t.Fatalf("Expected length %d, got %d", expected, got)
|
||||||
|
}
|
||||||
|
for _, r := range ingester.results {
|
||||||
|
expected, ok := out[r.Samples[0].Metric[model.MetricNameLabel]]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf(
|
||||||
|
"Unexpected metric name %q",
|
||||||
|
r.Samples[0].Metric[model.MetricNameLabel],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sort.Sort(expected.Samples)
|
||||||
|
sort.Sort(r.Samples)
|
||||||
|
if !expected.equal(r) {
|
||||||
|
t.Errorf("expected %s, got %s", expected, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue