Make Prometheus understand the new text format v0.0.4.

Change-Id: I42b834528c9c75d3d97443612bb05ce198ba4dc4
This commit is contained in:
Bjoern Rabenstein 2014-04-22 18:25:02 +02:00
parent 46fc7a3748
commit ad3452a46c
6 changed files with 195 additions and 23 deletions

View File

@ -40,7 +40,16 @@ func ProcessorForRequestHeader(header http.Header) (Processor, error) {
return nil, fmt.Errorf("Unsupported Encoding %s", params["encoding"])
}
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":
var prometheusApiVersion string

View File

@ -71,6 +71,21 @@ func testDiscriminatorHttpHeader(t test.Tester) {
output: nil,
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 {

View File

@ -31,10 +31,10 @@ type metricFamilyProcessor struct{}
//
// See http://godoc.org/github.com/matttproud/golang_protobuf_extensions/ext for
// more details.
var MetricFamilyProcessor = new(metricFamilyProcessor)
var MetricFamilyProcessor = &metricFamilyProcessor{}
func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *ProcessOptions) error {
family := new(dto.MetricFamily)
family := &dto.MetricFamily{}
for {
family.Reset()
@ -43,30 +43,33 @@ func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *Proc
if err == io.EOF {
return nil
}
return err
}
switch *family.Type {
case dto.MetricType_COUNTER:
if err := extractCounter(out, o, family); err != nil {
return err
}
case dto.MetricType_GAUGE:
if err := extractGauge(out, o, family); err != nil {
return err
}
case dto.MetricType_SUMMARY:
if err := extractSummary(out, o, family); err != nil {
return err
}
case dto.MetricType_UNTYPED:
if err := extractUntyped(out, o, family); err != nil {
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 {
case dto.MetricType_COUNTER:
if err := extractCounter(out, o, family); err != nil {
return err
}
case dto.MetricType_GAUGE:
if err := extractGauge(out, o, family); err != nil {
return err
}
case dto.MetricType_SUMMARY:
if err := extractSummary(out, o, family); err != nil {
return err
}
case dto.MetricType_UNTYPED:
if err := extractUntyped(out, o, family); err != nil {
return err
}
}
return nil
}

View File

@ -23,7 +23,8 @@ import (
// ProcessOptions dictates how the interpreted stream should be rendered for
// consumption.
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
}

View File

@ -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
}

View File

@ -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)
}
}
}