diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 43e9bf7a844..9dba1095481 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -477,31 +477,24 @@ "ImportPath": "github.com/pborman/uuid", "Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4" }, - { - "ImportPath": "github.com/prometheus/client_golang/extraction", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" - }, - { - "ImportPath": "github.com/prometheus/client_golang/model", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" - }, { "ImportPath": "github.com/prometheus/client_golang/prometheus", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" - }, - { - "ImportPath": "github.com/prometheus/client_golang/text", - "Comment": "0.4.0-1-g692492e", - "Rev": "692492e54b553a81013254cc1fba4b6dd76fad30" + "Comment": "0.7.0-39-g3b78d7a", + "Rev": "3b78d7a77f51ccbc364d4bc170920153022cfd08" }, { "ImportPath": "github.com/prometheus/client_model/go", "Comment": "model-0.0.2-12-gfa8ad6f", "Rev": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6" }, + { + "ImportPath": "github.com/prometheus/common/expfmt", + "Rev": "ef7a9a5fb138aa5d3a19988537606226869a0390" + }, + { + "ImportPath": "github.com/prometheus/common/model", + "Rev": "ef7a9a5fb138aa5d3a19988537606226869a0390" + }, { "ImportPath": "github.com/prometheus/procfs", "Rev": "490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d" diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/discriminator.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/discriminator.go deleted file mode 100644 index a353c6378ce..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/discriminator.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "errors" - "fmt" - "mime" - "net/http" -) - -// ProcessorForRequestHeader interprets a HTTP request header to determine -// what Processor should be used for the given input. If no acceptable -// Processor can be found, an error is returned. -func ProcessorForRequestHeader(header http.Header) (Processor, error) { - if header == nil { - return nil, errors.New("received illegal and nil header") - } - - mediatype, params, err := mime.ParseMediaType(header.Get("Content-Type")) - if err != nil { - return nil, fmt.Errorf("invalid Content-Type header %q: %s", header.Get("Content-Type"), err) - } - switch mediatype { - case "application/vnd.google.protobuf": - if params["proto"] != "io.prometheus.client.MetricFamily" { - return nil, fmt.Errorf("unrecognized protocol message %s", params["proto"]) - } - if params["encoding"] != "delimited" { - 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 - - if params["schema"] == "prometheus/telemetry" && params["version"] != "" { - prometheusAPIVersion = params["version"] - } else { - prometheusAPIVersion = header.Get("X-Prometheus-API-Version") - } - - switch prometheusAPIVersion { - case "0.0.2": - return Processor002, nil - case "0.0.1": - return Processor001, nil - default: - return nil, fmt.Errorf("unrecognized API version %s", prometheusAPIVersion) - } - default: - return nil, fmt.Errorf("unsupported media type %q, expected %q", mediatype, "application/json") - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/discriminator_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/discriminator_test.go deleted file mode 100644 index 4f08248d643..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/discriminator_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "errors" - "net/http" - "testing" -) - -func testDiscriminatorHTTPHeader(t testing.TB) { - var scenarios = []struct { - input map[string]string - output Processor - err error - }{ - { - output: nil, - err: errors.New("received illegal and nil header"), - }, - { - input: map[string]string{"Content-Type": "application/json", "X-Prometheus-API-Version": "0.0.0"}, - output: nil, - err: errors.New("unrecognized API version 0.0.0"), - }, - { - input: map[string]string{"Content-Type": "application/json", "X-Prometheus-API-Version": "0.0.1"}, - output: Processor001, - err: nil, - }, - { - input: map[string]string{"Content-Type": `application/json; schema="prometheus/telemetry"; version=0.0.0`}, - output: nil, - err: errors.New("unrecognized API version 0.0.0"), - }, - { - input: map[string]string{"Content-Type": `application/json; schema="prometheus/telemetry"; version=0.0.1`}, - output: Processor001, - err: nil, - }, - { - input: map[string]string{"Content-Type": `application/json; schema="prometheus/telemetry"; version=0.0.2`}, - output: Processor002, - err: nil, - }, - { - input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`}, - output: MetricFamilyProcessor, - err: nil, - }, - { - input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="illegal"; encoding="delimited"`}, - output: nil, - err: errors.New("unrecognized protocol message illegal"), - }, - { - input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="illegal"`}, - output: nil, - err: errors.New("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: errors.New("unrecognized API version 0.0.3"), - }, - } - - for i, scenario := range scenarios { - var header http.Header - - if len(scenario.input) > 0 { - header = http.Header{} - } - - for key, value := range scenario.input { - header.Add(key, value) - } - - actual, err := ProcessorForRequestHeader(header) - - if scenario.err != err { - if scenario.err != nil && err != nil { - if scenario.err.Error() != err.Error() { - t.Errorf("%d. expected %s, got %s", i, scenario.err, err) - } - } else if scenario.err != nil || err != nil { - t.Errorf("%d. expected %s, got %s", i, scenario.err, err) - } - } - - if scenario.output != actual { - t.Errorf("%d. expected %s, got %s", i, scenario.output, actual) - } - } -} - -func TestDiscriminatorHTTPHeader(t *testing.T) { - testDiscriminatorHTTPHeader(t) -} - -func BenchmarkDiscriminatorHTTPHeader(b *testing.B) { - for i := 0; i < b.N; i++ { - testDiscriminatorHTTPHeader(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/extraction.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/extraction.go deleted file mode 100644 index 31cdafad4bc..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/extraction.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2013 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 extraction decodes Prometheus clients' data streams for consumers. -package extraction diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/empty.json b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/empty.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/test0_0_1-0_0_2-large.json b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/test0_0_1-0_0_2-large.json deleted file mode 100644 index 7168338c8b0..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/test0_0_1-0_0_2-large.json +++ /dev/null @@ -1,1032 +0,0 @@ -[ - { - "baseLabels": { - "__name__": "rpc_calls_total_0", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_0" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_1", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_1" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_2", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_2" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_3", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_3" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_4", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_4" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_5", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_5" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_6", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_6" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_7", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_7" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_8", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_8" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_calls_total_9", - "job": "batch_job" - }, - "docstring": "Total count of RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": 25 - }, - { - "labels": { - "foo": "baz", - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds_9" - }, - "docstring": "RPC latency summary.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "foo": "bar", - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "foo": "bar", - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "foo": "bar", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - }, - { - "labels": { - "foo": "baz", - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - } -] diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/test0_0_1-0_0_2.json b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/test0_0_1-0_0_2.json deleted file mode 100644 index 1ac5be7d7ef..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/fixtures/test0_0_1-0_0_2.json +++ /dev/null @@ -1,79 +0,0 @@ -[ - { - "baseLabels": { - "__name__": "rpc_calls_total", - "job": "batch_job" - }, - "docstring": "RPC calls.", - "metric": { - "type": "counter", - "value": [ - { - "labels": { - "service": "zed" - }, - "value": 25 - }, - { - "labels": { - "service": "bar" - }, - "value": 25 - }, - { - "labels": { - "service": "foo" - }, - "value": 25 - } - ] - } - }, - { - "baseLabels": { - "__name__": "rpc_latency_microseconds" - }, - "docstring": "RPC latency.", - "metric": { - "type": "histogram", - "value": [ - { - "labels": { - "service": "foo" - }, - "value": { - "0.010000": 15.890724674774395, - "0.050000": 15.890724674774395, - "0.500000": 84.63044031436561, - "0.900000": 160.21100853053224, - "0.990000": 172.49828748957728 - } - }, - { - "labels": { - "service": "zed" - }, - "value": { - "0.010000": 0.0459814091918713, - "0.050000": 0.0459814091918713, - "0.500000": 0.6120456642749681, - "0.900000": 1.355915069887731, - "0.990000": 1.772733213161236 - } - }, - { - "labels": { - "service": "bar" - }, - "value": { - "0.010000": 78.48563317257356, - "0.050000": 78.48563317257356, - "0.500000": 97.31798360385088, - "0.900000": 109.89202084295582, - "0.990000": 109.99626121011262 - } - } - ] - } - } -] diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/metricfamilyprocessor.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/metricfamilyprocessor.go deleted file mode 100644 index 5a8f40bbf3c..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/metricfamilyprocessor.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "fmt" - "io" - "math" - - dto "github.com/prometheus/client_model/go" - - "github.com/matttproud/golang_protobuf_extensions/pbutil" - - "github.com/prometheus/client_golang/model" -) - -type metricFamilyProcessor struct{} - -// MetricFamilyProcessor decodes varint encoded record length-delimited streams -// of io.prometheus.client.MetricFamily. -// -// See http://godoc.org/github.com/matttproud/golang_protobuf_extensions/ext for -// more details. -var MetricFamilyProcessor = &metricFamilyProcessor{} - -func (m *metricFamilyProcessor) ProcessSingle(i io.Reader, out Ingester, o *ProcessOptions) error { - family := &dto.MetricFamily{} - - for { - family.Reset() - - if _, err := pbutil.ReadDelimited(i, family); err != nil { - if err == io.EOF { - return 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.GetType() { - 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 - } - case dto.MetricType_HISTOGRAM: - if err := extractHistogram(out, o, family); err != nil { - return err - } - } - return nil -} - -func extractCounter(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { - samples := make(model.Samples, 0, len(f.Metric)) - - for _, m := range f.Metric { - if m.Counter == nil { - continue - } - - sample := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Counter.GetValue()), - } - samples = append(samples, sample) - - if m.TimestampMs != nil { - sample.Timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) - } else { - sample.Timestamp = o.Timestamp - } - - metric := sample.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - } - - return out.Ingest(samples) -} - -func extractGauge(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { - samples := make(model.Samples, 0, len(f.Metric)) - - for _, m := range f.Metric { - if m.Gauge == nil { - continue - } - - sample := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Gauge.GetValue()), - } - samples = append(samples, sample) - - if m.TimestampMs != nil { - sample.Timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) - } else { - sample.Timestamp = o.Timestamp - } - - metric := sample.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - } - - return out.Ingest(samples) -} - -func extractSummary(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { - samples := make(model.Samples, 0, len(f.Metric)) - - for _, m := range f.Metric { - if m.Summary == nil { - continue - } - - timestamp := o.Timestamp - if m.TimestampMs != nil { - timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) - } - - for _, q := range m.Summary.Quantile { - sample := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(q.GetValue()), - Timestamp: timestamp, - } - samples = append(samples, sample) - - metric := sample.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - // BUG(matt): Update other names to "quantile". - metric[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile())) - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - } - - if m.Summary.SampleSum != nil { - sum := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Summary.GetSampleSum()), - Timestamp: timestamp, - } - samples = append(samples, sum) - - metric := sum.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") - } - - if m.Summary.SampleCount != nil { - count := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Summary.GetSampleCount()), - Timestamp: timestamp, - } - samples = append(samples, count) - - metric := count.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") - } - } - - return out.Ingest(samples) -} - -func extractUntyped(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { - samples := make(model.Samples, 0, len(f.Metric)) - - for _, m := range f.Metric { - if m.Untyped == nil { - continue - } - - sample := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Untyped.GetValue()), - } - samples = append(samples, sample) - - if m.TimestampMs != nil { - sample.Timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) - } else { - sample.Timestamp = o.Timestamp - } - - metric := sample.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - } - - return out.Ingest(samples) -} - -func extractHistogram(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { - samples := make(model.Samples, 0, len(f.Metric)) - - for _, m := range f.Metric { - if m.Histogram == nil { - continue - } - - timestamp := o.Timestamp - if m.TimestampMs != nil { - timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) - } - - infSeen := false - - for _, q := range m.Histogram.Bucket { - sample := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(q.GetCumulativeCount()), - Timestamp: timestamp, - } - samples = append(samples, sample) - - metric := sample.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound())) - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") - - if math.IsInf(q.GetUpperBound(), +1) { - infSeen = true - } - } - - if m.Histogram.SampleSum != nil { - sum := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Histogram.GetSampleSum()), - Timestamp: timestamp, - } - samples = append(samples, sum) - - metric := sum.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") - } - - if m.Histogram.SampleCount != nil { - count := &model.Sample{ - Metric: model.Metric{}, - Value: model.SampleValue(m.Histogram.GetSampleCount()), - Timestamp: timestamp, - } - samples = append(samples, count) - - metric := count.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") - - if !infSeen { - infBucket := &model.Sample{ - Metric: model.Metric{}, - Value: count.Value, - Timestamp: timestamp, - } - samples = append(samples, infBucket) - - metric := infBucket.Metric - for _, p := range m.Label { - metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) - } - metric[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf") - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") - } - } - } - - return out.Ingest(samples) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/metricfamilyprocessor_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/metricfamilyprocessor_test.go deleted file mode 100644 index 9ba0fdbce38..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/metricfamilyprocessor_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "sort" - "strings" - "testing" - - "github.com/prometheus/client_golang/model" -) - -var testTime = model.Now() - -type metricFamilyProcessorScenario struct { - in string - expected, actual []model.Samples -} - -func (s *metricFamilyProcessorScenario) Ingest(samples model.Samples) error { - s.actual = append(s.actual, samples) - return nil -} - -func (s *metricFamilyProcessorScenario) test(t *testing.T, set int) { - i := strings.NewReader(s.in) - - o := &ProcessOptions{ - Timestamp: testTime, - } - - err := MetricFamilyProcessor.ProcessSingle(i, s, o) - if err != nil { - t.Fatalf("%d. got error: %s", set, err) - } - - if len(s.expected) != len(s.actual) { - t.Fatalf("%d. expected length %d, got %d", set, len(s.expected), len(s.actual)) - } - - for i, expected := range s.expected { - sort.Sort(s.actual[i]) - sort.Sort(expected) - - if !expected.Equal(s.actual[i]) { - t.Errorf("%d.%d. expected %s, got %s", set, i, expected, s.actual[i]) - } - } -} - -func TestMetricFamilyProcessor(t *testing.T) { - scenarios := []metricFamilyProcessorScenario{ - { - in: "", - }, - { - in: "\x8f\x01\n\rrequest_count\x12\x12Number of requests\x18\x00\"0\n#\n\x0fsome_label_name\x12\x10some_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00E\xc0\"6\n)\n\x12another_label_name\x12\x13another_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00U@", - expected: []model.Samples{ - model.Samples{ - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_count", "some_label_name": "some_label_value"}, - Value: -42, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_count", "another_label_name": "another_label_value"}, - Value: 84, - Timestamp: testTime, - }, - }, - }, - }, - { - in: "\xb9\x01\n\rrequest_count\x12\x12Number of requests\x18\x02\"O\n#\n\x0fsome_label_name\x12\x10some_label_value\"(\x1a\x12\t\xaeG\xe1z\x14\xae\xef?\x11\x00\x00\x00\x00\x00\x00E\xc0\x1a\x12\t+\x87\x16\xd9\xce\xf7\xef?\x11\x00\x00\x00\x00\x00\x00U\xc0\"A\n)\n\x12another_label_name\x12\x13another_label_value\"\x14\x1a\x12\t\x00\x00\x00\x00\x00\x00\xe0?\x11\x00\x00\x00\x00\x00\x00$@", - expected: []model.Samples{ - model.Samples{ - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_count", "some_label_name": "some_label_value", "quantile": "0.99"}, - Value: -42, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_count", "some_label_name": "some_label_value", "quantile": "0.999"}, - Value: -84, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_count", "another_label_name": "another_label_value", "quantile": "0.5"}, - Value: 10, - Timestamp: testTime, - }, - }, - }, - }, - { - in: "\x8d\x01\n\x1drequest_duration_microseconds\x12\x15The response latency.\x18\x04\"S:Q\b\x85\x15\x11\xcd\xcc\xccL\x8f\xcb:A\x1a\v\b{\x11\x00\x00\x00\x00\x00\x00Y@\x1a\f\b\x9c\x03\x11\x00\x00\x00\x00\x00\x00^@\x1a\f\b\xd0\x04\x11\x00\x00\x00\x00\x00\x00b@\x1a\f\b\xf4\v\x11\x9a\x99\x99\x99\x99\x99e@\x1a\f\b\x85\x15\x11\x00\x00\x00\x00\x00\x00\xf0\u007f", - expected: []model.Samples{ - model.Samples{ - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "100"}, - Value: 123, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "120"}, - Value: 412, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "144"}, - Value: 592, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "172.8"}, - Value: 1524, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_bucket", "le": "+Inf"}, - Value: 2693, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_sum"}, - Value: 1756047.3, - Timestamp: testTime, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "request_duration_microseconds_count"}, - Value: 2693, - Timestamp: testTime, - }, - }, - }, - }, - } - - for i, scenario := range scenarios { - scenario.test(t, i) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor.go deleted file mode 100644 index 50c93c9e689..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "io" - "time" - - "github.com/prometheus/client_golang/model" -) - -// ProcessOptions dictates how the interpreted stream should be rendered for -// consumption. -type ProcessOptions struct { - // Timestamp is added to each value from the stream that has no explicit - // timestamp set. - Timestamp model.Timestamp -} - -// Ingester consumes result streams in whatever way is desired by the user. -type Ingester interface { - Ingest(model.Samples) error -} - -// Processor is responsible for decoding the actual message responses from -// stream into a format that can be consumed with the end result written -// to the results channel. -type Processor interface { - // ProcessSingle treats the input as a single self-contained message body and - // transforms it accordingly. It has no support for streaming. - ProcessSingle(in io.Reader, out Ingester, o *ProcessOptions) error -} - -// Helper function to convert map[string]string into LabelSet. -// -// NOTE: This should be deleted when support for go 1.0.3 is removed; 1.1 is -// smart enough to unmarshal JSON objects into LabelSet directly. -func labelSet(labels map[string]string) model.LabelSet { - labelset := make(model.LabelSet, len(labels)) - - for k, v := range labels { - labelset[model.LabelName(k)] = model.LabelValue(v) - } - - return labelset -} - -// A basic interface only useful in testing contexts for dispensing the time -// in a controlled manner. -type instantProvider interface { - // The current instant. - Now() time.Time -} - -// Clock is a simple means for fluently wrapping around standard Go timekeeping -// mechanisms to enhance testability without compromising code readability. -// -// It is sufficient for use on bare initialization. A provider should be -// set only for test contexts. When not provided, it emits the current -// system time. -type clock struct { - // The underlying means through which time is provided, if supplied. - Provider instantProvider -} - -// Emit the current instant. -func (t *clock) Now() time.Time { - if t.Provider == nil { - return time.Now() - } - - return t.Provider.Now() -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_1.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_1.go deleted file mode 100644 index 7a728efb0e2..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_1.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "encoding/json" - "fmt" - "io" - "io/ioutil" - - "github.com/prometheus/client_golang/model" -) - -const ( - baseLabels001 = "baseLabels" - counter001 = "counter" - docstring001 = "docstring" - gauge001 = "gauge" - histogram001 = "histogram" - labels001 = "labels" - metric001 = "metric" - type001 = "type" - value001 = "value" - percentile001 = "percentile" -) - -// Processor001 is responsible for decoding payloads from protocol version -// 0.0.1. -var Processor001 = &processor001{} - -// processor001 is responsible for handling API version 0.0.1. -type processor001 struct{} - -// entity001 represents a the JSON structure that 0.0.1 uses. -type entity001 []struct { - BaseLabels map[string]string `json:"baseLabels"` - Docstring string `json:"docstring"` - Metric struct { - MetricType string `json:"type"` - Value []struct { - Labels map[string]string `json:"labels"` - Value interface{} `json:"value"` - } `json:"value"` - } `json:"metric"` -} - -func (p *processor001) ProcessSingle(in io.Reader, out Ingester, o *ProcessOptions) error { - // TODO(matt): Replace with plain-jane JSON unmarshalling. - buffer, err := ioutil.ReadAll(in) - if err != nil { - return err - } - - entities := entity001{} - if err = json.Unmarshal(buffer, &entities); err != nil { - return err - } - - // TODO(matt): This outer loop is a great basis for parallelization. - pendingSamples := model.Samples{} - for _, entity := range entities { - for _, value := range entity.Metric.Value { - labels := labelSet(entity.BaseLabels).Merge(labelSet(value.Labels)) - - switch entity.Metric.MetricType { - case gauge001, counter001: - sampleValue, ok := value.Value.(float64) - if !ok { - return fmt.Errorf("could not convert value from %s %s to float64", entity, value) - } - - pendingSamples = append(pendingSamples, &model.Sample{ - Metric: model.Metric(labels), - Timestamp: o.Timestamp, - Value: model.SampleValue(sampleValue), - }) - - break - - case histogram001: - sampleValue, ok := value.Value.(map[string]interface{}) - if !ok { - return fmt.Errorf("could not convert value from %q to a map[string]interface{}", value.Value) - } - - for percentile, percentileValue := range sampleValue { - individualValue, ok := percentileValue.(float64) - if !ok { - return fmt.Errorf("could not convert value from %q to a float64", percentileValue) - } - - childMetric := make(map[model.LabelName]model.LabelValue, len(labels)+1) - - for k, v := range labels { - childMetric[k] = v - } - - childMetric[model.LabelName(percentile001)] = model.LabelValue(percentile) - - pendingSamples = append(pendingSamples, &model.Sample{ - Metric: model.Metric(childMetric), - Timestamp: o.Timestamp, - Value: model.SampleValue(individualValue), - }) - } - - break - } - } - } - if len(pendingSamples) > 0 { - return out.Ingest(pendingSamples) - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_1_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_1_test.go deleted file mode 100644 index b970b03e97f..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_1_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "errors" - "os" - "path" - "sort" - "testing" - - "github.com/prometheus/client_golang/model" -) - -var test001Time = model.Now() - -type testProcessor001ProcessScenario struct { - in string - expected, actual []model.Samples - err error -} - -func (s *testProcessor001ProcessScenario) Ingest(samples model.Samples) error { - s.actual = append(s.actual, samples) - return nil -} - -func (s *testProcessor001ProcessScenario) test(t testing.TB, set int) { - reader, err := os.Open(path.Join("fixtures", s.in)) - if err != nil { - t.Fatalf("%d. couldn't open scenario input file %s: %s", set, s.in, err) - } - - options := &ProcessOptions{ - Timestamp: test001Time, - } - err = Processor001.ProcessSingle(reader, s, options) - if s.err != err && (s.err == nil || err == nil || err.Error() != s.err.Error()) { - t.Fatalf("%d. expected err of %s, got %s", set, s.err, err) - } - - if len(s.actual) != len(s.expected) { - t.Fatalf("%d. expected output length of %d, got %d", set, len(s.expected), len(s.actual)) - } - - for i, expected := range s.expected { - sort.Sort(s.actual[i]) - sort.Sort(expected) - - if !expected.Equal(s.actual[i]) { - t.Errorf("%d.%d. expected %s, got %s", set, i, expected, s.actual[i]) - } - } -} - -func testProcessor001Process(t testing.TB) { - var scenarios = []testProcessor001ProcessScenario{ - { - in: "empty.json", - err: errors.New("unexpected end of JSON input"), - }, - { - in: "test0_0_1-0_0_2.json", - expected: []model.Samples{ - model.Samples{ - &model.Sample{ - Metric: model.Metric{"service": "zed", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job"}, - Value: 25, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"service": "bar", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job"}, - Value: 25, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"service": "foo", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job"}, - Value: 25, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 0.0459814091918713, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 78.48563317257356, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 15.890724674774395, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 0.0459814091918713, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 78.48563317257356, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 15.890724674774395, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 0.6120456642749681, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 97.31798360385088, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 84.63044031436561, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 1.355915069887731, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 109.89202084295582, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 160.21100853053224, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 1.772733213161236, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 109.99626121011262, - Timestamp: test001Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 172.49828748957728, - Timestamp: test001Time, - }, - }, - }, - }, - } - - for i, scenario := range scenarios { - scenario.test(t, i) - } -} - -func TestProcessor001Process(t *testing.T) { - testProcessor001Process(t) -} - -func BenchmarkProcessor001Process(b *testing.B) { - for i := 0; i < b.N; i++ { - testProcessor001Process(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_2.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_2.go deleted file mode 100644 index 24c7e81554c..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_2.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "encoding/json" - "fmt" - "io" - - "github.com/prometheus/client_golang/model" -) - -// Processor002 is responsible for decoding payloads from protocol version -// 0.0.2. -var Processor002 = &processor002{} - -type histogram002 struct { - Labels map[string]string `json:"labels"` - Values map[string]model.SampleValue `json:"value"` -} - -type counter002 struct { - Labels map[string]string `json:"labels"` - Value model.SampleValue `json:"value"` -} - -type processor002 struct{} - -func (p *processor002) ProcessSingle(in io.Reader, out Ingester, o *ProcessOptions) error { - // Processor for telemetry schema version 0.0.2. - // container for telemetry data - var entities []struct { - BaseLabels map[string]string `json:"baseLabels"` - Docstring string `json:"docstring"` - Metric struct { - Type string `json:"type"` - Values json.RawMessage `json:"value"` - } `json:"metric"` - } - - if err := json.NewDecoder(in).Decode(&entities); err != nil { - return err - } - - pendingSamples := model.Samples{} - for _, entity := range entities { - switch entity.Metric.Type { - case "counter", "gauge": - var values []counter002 - - if err := json.Unmarshal(entity.Metric.Values, &values); err != nil { - return fmt.Errorf("could not extract %s value: %s", entity.Metric.Type, err) - } - - for _, counter := range values { - labels := labelSet(entity.BaseLabels).Merge(labelSet(counter.Labels)) - - pendingSamples = append(pendingSamples, &model.Sample{ - Metric: model.Metric(labels), - Timestamp: o.Timestamp, - Value: counter.Value, - }) - } - - case "histogram": - var values []histogram002 - - if err := json.Unmarshal(entity.Metric.Values, &values); err != nil { - return fmt.Errorf("could not extract %s value: %s", entity.Metric.Type, err) - } - - for _, histogram := range values { - for percentile, value := range histogram.Values { - labels := labelSet(entity.BaseLabels).Merge(labelSet(histogram.Labels)) - labels[model.LabelName("percentile")] = model.LabelValue(percentile) - - pendingSamples = append(pendingSamples, &model.Sample{ - Metric: model.Metric(labels), - Timestamp: o.Timestamp, - Value: value, - }) - } - } - - default: - return fmt.Errorf("unknown metric type %q", entity.Metric.Type) - } - } - - if len(pendingSamples) > 0 { - return out.Ingest(pendingSamples) - } - - return nil -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_2_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_2_test.go deleted file mode 100644 index b2b75870208..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/processor0_0_2_test.go +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright 2013 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 extraction - -import ( - "bytes" - "errors" - "io/ioutil" - "os" - "path" - "runtime" - "sort" - "testing" - - "github.com/prometheus/client_golang/model" -) - -var test002Time = model.Now() - -type testProcessor002ProcessScenario struct { - in string - expected, actual []model.Samples - err error -} - -func (s *testProcessor002ProcessScenario) Ingest(samples model.Samples) error { - s.actual = append(s.actual, samples) - return nil -} - -func (s *testProcessor002ProcessScenario) test(t testing.TB, set int) { - reader, err := os.Open(path.Join("fixtures", s.in)) - if err != nil { - t.Fatalf("%d. couldn't open scenario input file %s: %s", set, s.in, err) - } - - options := &ProcessOptions{ - Timestamp: test002Time, - } - err = Processor002.ProcessSingle(reader, s, options) - if s.err != err && (s.err == nil || err == nil || err.Error() != s.err.Error()) { - t.Fatalf("%d. expected err of %s, got %s", set, s.err, err) - } - - if len(s.actual) != len(s.expected) { - t.Fatalf("%d. expected output length of %d, got %d", set, len(s.expected), len(s.actual)) - } - - for i, expected := range s.expected { - sort.Sort(s.actual[i]) - sort.Sort(expected) - - if !expected.Equal(s.actual[i]) { - t.Fatalf("%d.%d. expected %s, got %s", set, i, expected, s.actual[i]) - } - } -} - -func testProcessor002Process(t testing.TB) { - var scenarios = []testProcessor002ProcessScenario{ - { - in: "empty.json", - err: errors.New("EOF"), - }, - { - in: "test0_0_1-0_0_2.json", - expected: []model.Samples{ - model.Samples{ - &model.Sample{ - Metric: model.Metric{"service": "zed", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job"}, - Value: 25, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"service": "bar", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job"}, - Value: 25, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"service": "foo", model.MetricNameLabel: "rpc_calls_total", "job": "batch_job"}, - Value: 25, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 0.0459814091918713, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 78.48563317257356, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.010000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 15.890724674774395, - Timestamp: test002Time, - }, - &model.Sample{ - - Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 0.0459814091918713, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 78.48563317257356, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.050000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 15.890724674774395, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 0.6120456642749681, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 97.31798360385088, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.500000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 84.63044031436561, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 1.355915069887731, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 109.89202084295582, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.900000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 160.21100853053224, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "zed"}, - Value: 1.772733213161236, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "bar"}, - Value: 109.99626121011262, - Timestamp: test002Time, - }, - &model.Sample{ - Metric: model.Metric{"percentile": "0.990000", model.MetricNameLabel: "rpc_latency_microseconds", "service": "foo"}, - Value: 172.49828748957728, - Timestamp: test002Time, - }, - }, - }, - }, - } - - for i, scenario := range scenarios { - scenario.test(t, i) - } -} - -func TestProcessor002Process(t *testing.T) { - testProcessor002Process(t) -} - -func BenchmarkProcessor002Process(b *testing.B) { - b.StopTimer() - - pre := runtime.MemStats{} - runtime.ReadMemStats(&pre) - - b.StartTimer() - - for i := 0; i < b.N; i++ { - testProcessor002Process(b) - } - - post := runtime.MemStats{} - runtime.ReadMemStats(&post) - - allocated := post.TotalAlloc - pre.TotalAlloc - - b.Logf("Allocated %d at %f per cycle with %d cycles.", allocated, float64(allocated)/float64(b.N), b.N) -} - -func BenchmarkProcessor002ParseOnly(b *testing.B) { - b.StopTimer() - data, err := ioutil.ReadFile("fixtures/test0_0_1-0_0_2-large.json") - if err != nil { - b.Fatal(err) - } - ing := fakeIngester{} - b.StartTimer() - - for i := 0; i < b.N; i++ { - if err := Processor002.ProcessSingle(bytes.NewReader(data), ing, &ProcessOptions{}); err != nil { - b.Fatal(err) - } - } -} - -type fakeIngester struct{} - -func (i fakeIngester) Ingest(model.Samples) error { - return nil -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/textprocessor.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/textprocessor.go deleted file mode 100644 index 2eca1c63a60..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/textprocessor.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2014 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 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 -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/textprocessor_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/textprocessor_test.go deleted file mode 100644 index ff704a9bc3a..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/extraction/textprocessor_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2014 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 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]model.Samples{ - "mf1": model.Samples{ - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "mf1", "label": "value1"}, - Value: -3.14, - Timestamp: 123456, - }, - &model.Sample{ - Metric: model.Metric{model.MetricNameLabel: "mf1", "label": "value2"}, - Value: 42, - Timestamp: ts, - }, - }, - "mf2": 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 []model.Samples -} - -func (i *testIngester) Ingest(s model.Samples) error { - i.results = append(i.results, s) - 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[0].Metric[model.MetricNameLabel]] - if !ok { - t.Fatalf( - "Unexpected metric name %q", - r[0].Metric[model.MetricNameLabel], - ) - } - sort.Sort(expected) - sort.Sort(r) - if !expected.Equal(r) { - t.Errorf("expected %s, got %s", expected, r) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go deleted file mode 100644 index 75b2e79dae0..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 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 model - -import ( - "strings" -) - -const ( - // ExporterLabelPrefix is the label name prefix to prepend if a - // synthetic label is already present in the exported metrics. - ExporterLabelPrefix LabelName = "exporter_" - - // MetricNameLabel is the label name indicating the metric name of a - // timeseries. - MetricNameLabel LabelName = "__name__" - - // ReservedLabelPrefix is a prefix which is not legal in user-supplied - // label names. - ReservedLabelPrefix = "__" - - // JobLabel is the label name indicating the job from which a timeseries - // was scraped. - JobLabel LabelName = "job" - - // BucketLabel is used for the label that defines the upper bound of a - // bucket of a histogram ("le" -> "less or equal"). - BucketLabel = "le" - - // QuantileLabel is used for the label that defines the quantile in a - // summary. - QuantileLabel = "quantile" -) - -// A LabelName is a key for a LabelSet or Metric. It has a value associated -// therewith. -type LabelName string - -// LabelNames is a sortable LabelName slice. In implements sort.Interface. -type LabelNames []LabelName - -func (l LabelNames) Len() int { - return len(l) -} - -func (l LabelNames) Less(i, j int) bool { - return l[i] < l[j] -} - -func (l LabelNames) Swap(i, j int) { - l[i], l[j] = l[j], l[i] -} - -func (l LabelNames) String() string { - labelStrings := make([]string, 0, len(l)) - for _, label := range l { - labelStrings = append(labelStrings, string(label)) - } - return strings.Join(labelStrings, ", ") -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname_test.go deleted file mode 100644 index 693228d347c..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelname_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 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 model - -import ( - "sort" - "testing" -) - -func testLabelNames(t testing.TB) { - var scenarios = []struct { - in LabelNames - out LabelNames - }{ - { - in: LabelNames{"ZZZ", "zzz"}, - out: LabelNames{"ZZZ", "zzz"}, - }, - { - in: LabelNames{"aaa", "AAA"}, - out: LabelNames{"AAA", "aaa"}, - }, - } - - for i, scenario := range scenarios { - sort.Sort(scenario.in) - - for j, expected := range scenario.out { - if expected != scenario.in[j] { - t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) - } - } - } -} - -func TestLabelNames(t *testing.T) { - testLabelNames(t) -} - -func BenchmarkLabelNames(b *testing.B) { - for i := 0; i < b.N; i++ { - testLabelNames(b) - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelset.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelset.go deleted file mode 100644 index b1b54fba35f..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelset.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 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 model - -import ( - "fmt" - "sort" - "strings" -) - -// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet -// may be fully-qualified down to the point where it may resolve to a single -// Metric in the data store or not. All operations that occur within the realm -// of a LabelSet can emit a vector of Metric entities to which the LabelSet may -// match. -type LabelSet map[LabelName]LabelValue - -// Merge is a helper function to non-destructively merge two label sets. -func (l LabelSet) Merge(other LabelSet) LabelSet { - result := make(LabelSet, len(l)) - - for k, v := range l { - result[k] = v - } - - for k, v := range other { - result[k] = v - } - - return result -} - -func (l LabelSet) String() string { - labelStrings := make([]string, 0, len(l)) - for label, value := range l { - labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value)) - } - - switch len(labelStrings) { - case 0: - return "" - default: - sort.Strings(labelStrings) - return fmt.Sprintf("{%s}", strings.Join(labelStrings, ", ")) - } -} - -// MergeFromMetric merges Metric into this LabelSet. -func (l LabelSet) MergeFromMetric(m Metric) { - for k, v := range m { - l[k] = v - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go deleted file mode 100644 index d51b1842f6a..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2013 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 model - -import "testing" - -func testMetric(t testing.TB) { - var scenarios = []struct { - input Metric - fingerprint Fingerprint - }{ - { - input: Metric{}, - fingerprint: 14695981039346656037, - }, - { - input: Metric{ - "first_name": "electro", - "occupation": "robot", - "manufacturer": "westinghouse", - }, - fingerprint: 11310079640881077873, - }, - { - input: Metric{ - "x": "y", - }, - fingerprint: 13948396922932177635, - }, - { - input: Metric{ - "a": "bb", - "b": "c", - }, - fingerprint: 3198632812309449502, - }, - { - input: Metric{ - "a": "b", - "bb": "c", - }, - fingerprint: 5774953389407657638, - }, - } - - for i, scenario := range scenarios { - if scenario.fingerprint != scenario.input.Fingerprint() { - t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, scenario.input.Fingerprint()) - } - } -} - -func TestMetric(t *testing.T) { - testMetric(t) -} - -func BenchmarkMetric(b *testing.B) { - for i := 0; i < b.N; i++ { - testMetric(b) - } -} - -func TestCOWMetric(t *testing.T) { - testMetric := Metric{ - "to_delete": "test1", - "to_change": "test2", - } - - scenarios := []struct { - fn func(*COWMetric) - out Metric - }{ - { - fn: func(cm *COWMetric) { - cm.Delete("to_delete") - }, - out: Metric{ - "to_change": "test2", - }, - }, - { - fn: func(cm *COWMetric) { - cm.Set("to_change", "changed") - }, - out: Metric{ - "to_delete": "test1", - "to_change": "changed", - }, - }, - } - - for i, s := range scenarios { - orig := testMetric.Clone() - cm := &COWMetric{ - Metric: orig, - } - - s.fn(cm) - - // Test that the original metric was not modified. - if !orig.Equal(testMetric) { - t.Fatalf("%d. original metric changed; expected %v, got %v", i, testMetric, orig) - } - - // Test that the new metric has the right changes. - if !cm.Metric.Equal(s.out) { - t.Fatalf("%d. copied metric doesn't contain expected changes; expected %v, got %v", i, s.out, cm.Metric) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample.go deleted file mode 100644 index c13a44d95c9..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 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 model - -// Sample is a sample value with a timestamp and a metric. -type Sample struct { - Metric Metric - Value SampleValue - Timestamp Timestamp -} - -// Equal compares first the metrics, then the timestamp, then the value. -func (s *Sample) Equal(o *Sample) bool { - if s == o { - return true - } - - if !s.Metric.Equal(o.Metric) { - return false - } - if !s.Timestamp.Equal(o.Timestamp) { - return false - } - if !s.Value.Equal(o.Value) { - return false - } - - return true -} - -// Samples is a sortable Sample slice. It implements sort.Interface. -type Samples []*Sample - -func (s Samples) Len() int { - return len(s) -} - -// Less compares first the metrics, then the timestamp. -func (s Samples) Less(i, j int) bool { - switch { - case s[i].Metric.Before(s[j].Metric): - return true - case s[j].Metric.Before(s[i].Metric): - return false - case s[i].Timestamp.Before(s[j].Timestamp): - return true - default: - return false - } -} - -func (s Samples) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// Equal compares two sets of samples and returns true if they are equal. -func (s Samples) Equal(o Samples) bool { - if len(s) != len(o) { - return false - } - - for i, sample := range s { - if !sample.Equal(o[i]) { - return false - } - } - return true -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go deleted file mode 100644 index 3dc4ad2511a..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/sample_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2013 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 model - -import ( - "sort" - "testing" -) - -func TestSamplesSort(t *testing.T) { - input := Samples{ - &Sample{ - // Fingerprint: 81f9c9ed24563f8f. - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 81f9c9ed24563f8f. - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 2, - }, - &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 2, - }, - &Sample{ - // Fingerprint: 68f4c9ed24533f8f. - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 68f4c9ed24533f8f. - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 2, - }, - } - - expected := Samples{ - &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 1bf6c9ed24543f8f. - Metric: Metric{ - MetricNameLabel: "C", - }, - Timestamp: 2, - }, - &Sample{ - // Fingerprint: 68f4c9ed24533f8f. - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 68f4c9ed24533f8f. - Metric: Metric{ - MetricNameLabel: "B", - }, - Timestamp: 2, - }, - &Sample{ - // Fingerprint: 81f9c9ed24563f8f. - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 1, - }, - &Sample{ - // Fingerprint: 81f9c9ed24563f8f. - Metric: Metric{ - MetricNameLabel: "A", - }, - Timestamp: 2, - }, - } - - sort.Sort(input) - - for i, actual := range input { - actualFp := actual.Metric.Fingerprint() - expectedFp := expected[i].Metric.Fingerprint() - - if !actualFp.Equal(expectedFp) { - t.Fatalf("%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String()) - } - - if actual.Timestamp != expected[i].Timestamp { - t.Fatalf("%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp) - } - } -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/samplevalue.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/samplevalue.go deleted file mode 100644 index 469c2c0b0e5..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/samplevalue.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2013 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 model - -import ( - "fmt" - "strconv" -) - -// A SampleValue is a representation of a value for a given sample at a given -// time. -type SampleValue float64 - -// Equal does a straight v==o. -func (v SampleValue) Equal(o SampleValue) bool { - return v == o -} - -// MarshalJSON implements json.Marshaler. -func (v SampleValue) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf(`"%s"`, v)), nil -} - -func (v SampleValue) String() string { - return strconv.FormatFloat(float64(v), 'f', -1, 64) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/timestamp.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/model/timestamp.go deleted file mode 100644 index afffdcf753e..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/timestamp.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 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 model - -import ( - "math" - "strconv" - - native_time "time" -) - -// Timestamp is the number of milliseconds since the epoch -// (1970-01-01 00:00 UTC) excluding leap seconds. -type Timestamp int64 - -const ( - // MinimumTick is the minimum supported time resolution. This has to be - // at least native_time.Second in order for the code below to work. - MinimumTick = native_time.Millisecond - // second is the timestamp duration equivalent to one second. - second = int64(native_time.Second / MinimumTick) - // The number of nanoseconds per minimum tick. - nanosPerTick = int64(MinimumTick / native_time.Nanosecond) - - // Earliest is the earliest timestamp representable. Handy for - // initializing a high watermark. - Earliest = Timestamp(math.MinInt64) - // Latest is the latest timestamp representable. Handy for initializing - // a low watermark. - Latest = Timestamp(math.MaxInt64) -) - -// Equal reports whether two timestamps represent the same instant. -func (t Timestamp) Equal(o Timestamp) bool { - return t == o -} - -// Before reports whether the timestamp t is before o. -func (t Timestamp) Before(o Timestamp) bool { - return t < o -} - -// After reports whether the timestamp t is after o. -func (t Timestamp) After(o Timestamp) bool { - return t > o -} - -// Add returns the Timestamp t + d. -func (t Timestamp) Add(d native_time.Duration) Timestamp { - return t + Timestamp(d/MinimumTick) -} - -// Sub returns the Duration t - o. -func (t Timestamp) Sub(o Timestamp) native_time.Duration { - return native_time.Duration(t-o) * MinimumTick -} - -// Time returns the time.Time representation of t. -func (t Timestamp) Time() native_time.Time { - return native_time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick) -} - -// Unix returns t as a Unix time, the number of seconds elapsed -// since January 1, 1970 UTC. -func (t Timestamp) Unix() int64 { - return int64(t) / second -} - -// UnixNano returns t as a Unix time, the number of nanoseconds elapsed -// since January 1, 1970 UTC. -func (t Timestamp) UnixNano() int64 { - return int64(t) * nanosPerTick -} - -// String returns a string representation of the timestamp. -func (t Timestamp) String() string { - return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64) -} - -// MarshalJSON implements the json.Marshaler interface. -func (t Timestamp) MarshalJSON() ([]byte, error) { - return []byte(t.String()), nil -} - -// Now returns the current time as a Timestamp. -func Now() Timestamp { - return TimestampFromTime(native_time.Now()) -} - -// TimestampFromTime returns the Timestamp equivalent to the time.Time t. -func TimestampFromTime(t native_time.Time) Timestamp { - return TimestampFromUnixNano(t.UnixNano()) -} - -// TimestampFromUnix returns the Timestamp equivalent to the Unix timestamp t -// provided in seconds. -func TimestampFromUnix(t int64) Timestamp { - return Timestamp(t * second) -} - -// TimestampFromUnixNano returns the Timestamp equivalent to the Unix timestamp -// t provided in nanoseconds. -func TimestampFromUnixNano(t int64) Timestamp { - return Timestamp(t / nanosPerTick) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go index f8d633fbd55..a2952d1c881 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go @@ -33,7 +33,7 @@ type Counter interface { // Set is used to set the Counter to an arbitrary value. It is only used // if you have to transfer a value from an external counter into this - // Prometheus metrics. Do not use it for regular handling of a + // Prometheus metric. Do not use it for regular handling of a // Prometheus counter (as it can be used to break the contract of // monotonically increasing values). Set(float64) diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go index 7e1f9e85372..fcde784d640 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go @@ -9,18 +9,20 @@ import ( "sort" "strings" - "github.com/prometheus/client_golang/model" + "github.com/golang/protobuf/proto" dto "github.com/prometheus/client_model/go" - - "github.com/golang/protobuf/proto" ) var ( metricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`) - labelNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`) + labelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") ) +// reservedLabelPrefix is a prefix which is not legal in user-supplied +// label names. +const reservedLabelPrefix = "__" + // Labels represents a collection of label name -> value mappings. This type is // commonly used with the With(Labels) and GetMetricWith(Labels) methods of // metric vector Collectors, e.g.: @@ -134,7 +136,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * for _, val := range labelValues { b.Reset() b.WriteString(val) - b.WriteByte(model.SeparatorByte) + b.WriteByte(separatorByte) h.Write(b.Bytes()) } d.id = h.Sum64() @@ -145,12 +147,12 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * h.Reset() b.Reset() b.WriteString(help) - b.WriteByte(model.SeparatorByte) + b.WriteByte(separatorByte) h.Write(b.Bytes()) for _, labelName := range labelNames { b.Reset() b.WriteString(labelName) - b.WriteByte(model.SeparatorByte) + b.WriteByte(separatorByte) h.Write(b.Bytes()) } d.dimHash = h.Sum64() @@ -195,5 +197,5 @@ func (d *Desc) String() string { func checkLabelName(l string) bool { return labelNameRE.MatchString(l) && - !strings.HasPrefix(l, model.ReservedLabelPrefix) + !strings.HasPrefix(l, reservedLabelPrefix) } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go index b98c135262a..425fe8793cd 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go @@ -61,12 +61,13 @@ // It also exports some stats about the HTTP usage of the /metrics // endpoint. (See the Handler function for more detail.) // -// A more advanced metric type is the Summary. +// Two more advanced metric types are the Summary and Histogram. // -// In addition to the fundamental metric types Gauge, Counter, and Summary, a -// very important part of the Prometheus data model is the partitioning of -// samples along dimensions called labels, which results in metric vectors. The -// fundamental types are GaugeVec, CounterVec, and SummaryVec. +// In addition to the fundamental metric types Gauge, Counter, Summary, and +// Histogram, a very important part of the Prometheus data model is the +// partitioning of samples along dimensions called labels, which results in +// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec, +// and HistogramVec. // // Those are all the parts needed for basic usage. Detailed documentation and // examples are provided below. diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go index d106c42c6b7..0344e465b0c 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go @@ -392,6 +392,9 @@ func ExampleSummaryVec() { temps.WithLabelValues("lithobates-catesbeianus").Observe(32 + math.Floor(100*math.Cos(float64(i)*0.11))/10) } + // Create a Summary without any observations. + temps.WithLabelValues("leiopelma-hochstetteri") + // Just for demonstration, let's check the state of the summary vector // by (ab)using its Collect method and the Write method of its elements // (which is usually only used by Prometheus internally - code like the @@ -414,6 +417,26 @@ func ExampleSummaryVec() { // Output: // [label: < // name: "species" + // value: "leiopelma-hochstetteri" + // > + // summary: < + // sample_count: 0 + // sample_sum: 0 + // quantile: < + // quantile: 0.5 + // value: nan + // > + // quantile: < + // quantile: 0.9 + // value: nan + // > + // quantile: < + // quantile: 0.99 + // value: nan + // > + // > + // label: < + // name: "species" // value: "lithobates-catesbeianus" // > // summary: < @@ -455,6 +478,56 @@ func ExampleSummaryVec() { // ] } +func ExampleConstSummary() { + desc := prometheus.NewDesc( + "http_request_duration_seconds", + "A summary of the HTTP request durations.", + []string{"code", "method"}, + prometheus.Labels{"owner": "example"}, + ) + + // Create a constant summary from values we got from a 3rd party telemetry system. + s := prometheus.MustNewConstSummary( + desc, + 4711, 403.34, + map[float64]float64{0.5: 42.3, 0.9: 323.3}, + "200", "get", + ) + + // Just for demonstration, let's check the state of the summary by + // (ab)using its Write method (which is usually only used by Prometheus + // internally). + metric := &dto.Metric{} + s.Write(metric) + fmt.Println(proto.MarshalTextString(metric)) + + // Output: + // label: < + // name: "code" + // value: "200" + // > + // label: < + // name: "method" + // value: "get" + // > + // label: < + // name: "owner" + // value: "example" + // > + // summary: < + // sample_count: 4711 + // sample_sum: 403.34 + // quantile: < + // quantile: 0.5 + // value: 42.3 + // > + // quantile: < + // quantile: 0.9 + // value: 323.3 + // > + // > +} + func ExampleHistogram() { temps := prometheus.NewHistogram(prometheus.HistogramOpts{ Name: "pond_temperature_celsius", @@ -501,6 +574,64 @@ func ExampleHistogram() { // > } +func ExampleConstHistogram() { + desc := prometheus.NewDesc( + "http_request_duration_seconds", + "A histogram of the HTTP request durations.", + []string{"code", "method"}, + prometheus.Labels{"owner": "example"}, + ) + + // Create a constant histogram from values we got from a 3rd party telemetry system. + h := prometheus.MustNewConstHistogram( + desc, + 4711, 403.34, + map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233}, + "200", "get", + ) + + // Just for demonstration, let's check the state of the histogram by + // (ab)using its Write method (which is usually only used by Prometheus + // internally). + metric := &dto.Metric{} + h.Write(metric) + fmt.Println(proto.MarshalTextString(metric)) + + // Output: + // label: < + // name: "code" + // value: "200" + // > + // label: < + // name: "method" + // value: "get" + // > + // label: < + // name: "owner" + // value: "example" + // > + // histogram: < + // sample_count: 4711 + // sample_sum: 403.34 + // bucket: < + // cumulative_count: 121 + // upper_bound: 25 + // > + // bucket: < + // cumulative_count: 2403 + // upper_bound: 50 + // > + // bucket: < + // cumulative_count: 3221 + // upper_bound: 100 + // > + // bucket: < + // cumulative_count: 4233 + // upper_bound: 200 + // > + // > +} + func ExamplePushCollectors() { hostname, _ := os.Hostname() completionTime := prometheus.NewGauge(prometheus.GaugeOpts{ diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go index d7b7a20a123..85fa20be455 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go @@ -2,10 +2,13 @@ package prometheus import ( "runtime" + "runtime/debug" + "time" ) type goCollector struct { goroutines Gauge + gcDesc *Desc } // NewGoCollector returns a collector which exports metrics about the current @@ -13,19 +16,35 @@ type goCollector struct { func NewGoCollector() *goCollector { return &goCollector{ goroutines: NewGauge(GaugeOpts{ - Name: "process_goroutines", + Name: "go_goroutines", Help: "Number of goroutines that currently exist.", }), + gcDesc: NewDesc( + "go_gc_duration_seconds", + "A summary of the GC invocation durations.", + nil, nil), } } // Describe returns all descriptions of the collector. func (c *goCollector) Describe(ch chan<- *Desc) { ch <- c.goroutines.Desc() + ch <- c.gcDesc } // Collect returns the current state of all metrics of the collector. func (c *goCollector) Collect(ch chan<- Metric) { c.goroutines.Set(float64(runtime.NumGoroutine())) ch <- c.goroutines + + var stats debug.GCStats + stats.PauseQuantiles = make([]time.Duration, 5) + debug.ReadGCStats(&stats) + + quantiles := make(map[float64]float64) + for idx, pq := range stats.PauseQuantiles[1:] { + quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds() + } + quantiles[0.0] = stats.PauseQuantiles[0].Seconds() + ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles) } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go index b0582d1b98b..9a8858cbd2b 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go @@ -1,7 +1,7 @@ package prometheus import ( - "reflect" + "runtime" "testing" "time" @@ -35,6 +35,9 @@ func TestGoCollector(t *testing.T) { case Gauge: pb := &dto.Metric{} m.Write(pb) + if pb.GetGauge() == nil { + continue + } if old == -1 { old = int(pb.GetGauge().GetValue()) @@ -47,9 +50,71 @@ func TestGoCollector(t *testing.T) { t.Errorf("want 1 new goroutine, got %d", diff) } + // GoCollector performs two sends per call. + // On line 27 we need to receive the second send + // to shut down cleanly. + <-ch + return + } + case <-time.After(1 * time.Second): + t.Fatalf("expected collect timed out") + } + } +} + +func TestGCCollector(t *testing.T) { + var ( + c = NewGoCollector() + ch = make(chan Metric) + waitc = make(chan struct{}) + closec = make(chan struct{}) + oldGC uint64 + oldPause float64 + ) + defer close(closec) + + go func() { + c.Collect(ch) + // force GC + runtime.GC() + <-waitc + c.Collect(ch) + }() + + first := true + for { + select { + case metric := <-ch: + switch m := metric.(type) { + case *constSummary, *value: + pb := &dto.Metric{} + m.Write(pb) + if pb.GetSummary() == nil { + continue + } + + if len(pb.GetSummary().Quantile) != 5 { + t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile)) + } + for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} { + if *pb.GetSummary().Quantile[idx].Quantile != want { + t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want) + } + } + if first { + first = false + oldGC = *pb.GetSummary().SampleCount + oldPause = *pb.GetSummary().SampleSum + close(waitc) + continue + } + if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 { + t.Errorf("want 1 new garbage collection run, got %d", diff) + } + if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 { + t.Errorf("want moar pause, got %f", diff) + } return - default: - t.Errorf("want type Gauge, got %s", reflect.TypeOf(metric)) } case <-time.After(1 * time.Second): t.Fatalf("expected collect timed out") diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go index 9b36a611506..f98a41bc89d 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go @@ -22,7 +22,6 @@ import ( "github.com/golang/protobuf/proto" - "github.com/prometheus/client_golang/model" dto "github.com/prometheus/client_model/go" ) @@ -49,6 +48,10 @@ type Histogram interface { Observe(float64) } +// bucketLabel is used for the label that defines the upper bound of a +// bucket of a histogram ("le" -> "less or equal"). +const bucketLabel = "le" + var ( // DefBuckets are the default Histogram buckets. The default buckets are // tailored to broadly measure the response time (in seconds) of a @@ -57,7 +60,7 @@ var ( DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10} errBucketLabelNotAllowed = fmt.Errorf( - "%q is not allowed as label name in histograms", model.BucketLabel, + "%q is not allowed as label name in histograms", bucketLabel, ) ) @@ -147,7 +150,7 @@ type HistogramOpts struct { // element in the slice is the upper inclusive bound of a bucket. The // values must be sorted in strictly increasing order. There is no need // to add a highest bucket with +Inf bound, it will be added - // implicitly. The default value is DefObjectives. + // implicitly. The default value is DefBuckets. Buckets []float64 } @@ -171,12 +174,12 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr } for _, n := range desc.variableLabels { - if n == model.BucketLabel { + if n == bucketLabel { panic(errBucketLabelNotAllowed) } } for _, lp := range desc.constLabelPairs { - if lp.GetName() == model.BucketLabel { + if lp.GetName() == bucketLabel { panic(errBucketLabelNotAllowed) } } @@ -213,6 +216,13 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr } type histogram struct { + // sumBits contains the bits of the float64 representing the sum of all + // observations. sumBits and count have to go first in the struct to + // guarantee alignment for atomic operations. + // http://golang.org/pkg/sync/atomic/#pkg-note-BUG + sumBits uint64 + count uint64 + SelfCollector // Note that there is no mutex required. @@ -222,9 +232,6 @@ type histogram struct { counts []uint64 labelPairs []*dto.LabelPair - - sumBits uint64 // The bits of the float64 representing the sum of all observations. - count uint64 } func (h *histogram) Desc() *Desc { @@ -342,3 +349,102 @@ func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram { func (m *HistogramVec) With(labels Labels) Histogram { return m.MetricVec.With(labels).(Histogram) } + +type constHistogram struct { + desc *Desc + count uint64 + sum float64 + buckets map[float64]uint64 + labelPairs []*dto.LabelPair +} + +func (h *constHistogram) Desc() *Desc { + return h.desc +} + +func (h *constHistogram) Write(out *dto.Metric) error { + his := &dto.Histogram{} + buckets := make([]*dto.Bucket, 0, len(h.buckets)) + + his.SampleCount = proto.Uint64(h.count) + his.SampleSum = proto.Float64(h.sum) + + for upperBound, count := range h.buckets { + buckets = append(buckets, &dto.Bucket{ + CumulativeCount: proto.Uint64(count), + UpperBound: proto.Float64(upperBound), + }) + } + + if len(buckets) > 0 { + sort.Sort(buckSort(buckets)) + } + his.Bucket = buckets + + out.Histogram = his + out.Label = h.labelPairs + + return nil +} + +// NewConstHistogram returns a metric representing a Prometheus histogram with +// fixed values for the count, sum, and bucket counts. As those parameters +// cannot be changed, the returned value does not implement the Histogram +// interface (but only the Metric interface). Users of this package will not +// have much use for it in regular operations. However, when implementing custom +// Collectors, it is useful as a throw-away metric that is generated on the fly +// to send it to Prometheus in the Collect method. +// +// buckets is a map of upper bounds to cumulative counts, excluding the +Inf +// bucket. +// +// NewConstHistogram returns an error if the length of labelValues is not +// consistent with the variable labels in Desc. +func NewConstHistogram( + desc *Desc, + count uint64, + sum float64, + buckets map[float64]uint64, + labelValues ...string, +) (Metric, error) { + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality + } + return &constHistogram{ + desc: desc, + count: count, + sum: sum, + buckets: buckets, + labelPairs: makeLabelPairs(desc, labelValues), + }, nil +} + +// MustNewConstHistogram is a version of NewConstHistogram that panics where +// NewConstMetric would have returned an error. +func MustNewConstHistogram( + desc *Desc, + count uint64, + sum float64, + buckets map[float64]uint64, + labelValues ...string, +) Metric { + m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...) + if err != nil { + panic(err) + } + return m +} + +type buckSort []*dto.Bucket + +func (s buckSort) Len() int { + return len(s) +} + +func (s buckSort) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s buckSort) Less(i, j int) bool { + return s[i].GetUpperBound() < s[j].GetUpperBound() +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go index 855af469501..11cf66b4fe2 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go @@ -124,6 +124,10 @@ func BenchmarkHistogramWrite8(b *testing.B) { var testBuckets = []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)} func TestHistogramConcurrency(t *testing.T) { + if testing.Short() { + t.Skip("Skipping test in short mode.") + } + rand.Seed(42) it := func(n uint32) bool { @@ -198,6 +202,10 @@ func TestHistogramConcurrency(t *testing.T) { } func TestHistogramVecConcurrency(t *testing.T) { + if testing.Short() { + t.Skip("Skipping test in short mode.") + } + rand.Seed(42) objectives := make([]float64, 0, len(DefObjectives)) diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go index dac92fd907c..eabe602468f 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go @@ -14,6 +14,9 @@ package prometheus import ( + "bufio" + "io" + "net" "net/http" "strconv" "strings" @@ -141,7 +144,18 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo urlLen = len(r.URL.String()) } go computeApproximateRequestSize(r, out, urlLen) - handlerFunc(delegate, r) + + _, cn := w.(http.CloseNotifier) + _, fl := w.(http.Flusher) + _, hj := w.(http.Hijacker) + _, rf := w.(io.ReaderFrom) + var rw http.ResponseWriter + if cn && fl && hj && rf { + rw = &fancyResponseWriterDelegator{delegate} + } else { + rw = delegate + } + handlerFunc(rw, r) elapsed := float64(time.Since(now)) / float64(time.Microsecond) @@ -178,7 +192,7 @@ type responseWriterDelegator struct { handler, method string status int - written int + written int64 wroteHeader bool } @@ -193,7 +207,32 @@ func (r *responseWriterDelegator) Write(b []byte) (int, error) { r.WriteHeader(http.StatusOK) } n, err := r.ResponseWriter.Write(b) - r.written += n + r.written += int64(n) + return n, err +} + +type fancyResponseWriterDelegator struct { + *responseWriterDelegator +} + +func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool { + return f.ResponseWriter.(http.CloseNotifier).CloseNotify() +} + +func (f *fancyResponseWriterDelegator) Flush() { + f.ResponseWriter.(http.Flusher).Flush() +} + +func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) { + return f.ResponseWriter.(http.Hijacker).Hijack() +} + +func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) { + if !f.wroteHeader { + f.WriteHeader(http.StatusOK) + } + n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r) + f.written += n return n, err } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go index d8905de2e88..86fd81c108b 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go @@ -19,6 +19,8 @@ import ( dto "github.com/prometheus/client_model/go" ) +const separatorByte byte = 255 + // A Metric models a single sample value with its meta data being exported to // Prometheus. Implementers of Metric in this package inclued Gauge, Counter, // Untyped, and Summary. Users can implement their own Metric types, but that diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go index 2515311b94d..5970aaeeba9 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go @@ -33,13 +33,10 @@ import ( "strings" "sync" - "bitbucket.org/ww/goautoneg" "github.com/golang/protobuf/proto" + "github.com/prometheus/common/expfmt" dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/model" - "github.com/prometheus/client_golang/text" ) var ( @@ -170,6 +167,11 @@ func Unregister(c Collector) bool { // checks are performed, but no further consistency checks (which would require // knowledge of a metric descriptor). // +// Sorting concerns: The caller is responsible for sorting the label pairs in +// each metric. However, the order of metrics will be sorted by the registry as +// it is required anyway after merging with the metric families collected +// conventionally. +// // The function must be callable at any time and concurrently. func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) { defRegistry.metricFamilyInjectionHook = hook @@ -341,7 +343,7 @@ func (r *registry) Push(job, instance, pushURL, method string) error { } buf := r.getBuf() defer r.giveBuf(buf) - if _, err := r.writePB(buf, text.WriteProtoDelimited); err != nil { + if err := r.writePB(expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)); err != nil { if r.panicOnCollectError { panic(err) } @@ -364,11 +366,11 @@ func (r *registry) Push(job, instance, pushURL, method string) error { } func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) { - enc, contentType := chooseEncoder(req) + contentType := expfmt.Negotiate(req.Header) buf := r.getBuf() defer r.giveBuf(buf) writer, encoding := decorateWriter(req, buf) - if _, err := r.writePB(writer, enc); err != nil { + if err := r.writePB(expfmt.NewEncoder(writer, contentType)); err != nil { if r.panicOnCollectError { panic(err) } @@ -379,7 +381,7 @@ func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) { closer.Close() } header := w.Header() - header.Set(contentTypeHeader, contentType) + header.Set(contentTypeHeader, string(contentType)) header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) if encoding != "" { header.Set(contentEncodingHeader, encoding) @@ -387,7 +389,7 @@ func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Write(buf.Bytes()) } -func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) { +func (r *registry) writePB(encoder expfmt.Encoder) error { var metricHashes map[uint64]struct{} if r.collectChecksEnabled { metricHashes = make(map[uint64]struct{}) @@ -439,7 +441,7 @@ func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) { // TODO: Consider different means of error reporting so // that a single erroneous metric could be skipped // instead of blowing up the whole collection. - return 0, fmt.Errorf("error collecting metric %v: %s", desc, err) + return fmt.Errorf("error collecting metric %v: %s", desc, err) } switch { case metricFamily.Type != nil: @@ -455,11 +457,11 @@ func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) { case dtoMetric.Histogram != nil: metricFamily.Type = dto.MetricType_HISTOGRAM.Enum() default: - return 0, fmt.Errorf("empty metric collected: %s", dtoMetric) + return fmt.Errorf("empty metric collected: %s", dtoMetric) } if r.collectChecksEnabled { if err := r.checkConsistency(metricFamily, dtoMetric, desc, metricHashes); err != nil { - return 0, err + return err } } metricFamily.Metric = append(metricFamily.Metric, dtoMetric) @@ -473,7 +475,7 @@ func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) { if r.collectChecksEnabled { for _, m := range mf.Metric { if err := r.checkConsistency(mf, m, nil, metricHashes); err != nil { - return 0, err + return err } } } @@ -482,7 +484,7 @@ func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) { for _, m := range mf.Metric { if r.collectChecksEnabled { if err := r.checkConsistency(existingMF, m, nil, metricHashes); err != nil { - return 0, err + return err } } existingMF.Metric = append(existingMF.Metric, m) @@ -503,15 +505,12 @@ func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) { } sort.Strings(names) - var written int for _, name := range names { - w, err := writeEncoded(w, metricFamiliesByName[name]) - written += w - if err != nil { - return written, err + if err := encoder.Encode(metricFamiliesByName[name]); err != nil { + return err } } - return written, nil + return nil } func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *dto.Metric, desc *Desc, metricHashes map[uint64]struct{}) error { @@ -520,10 +519,11 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil || metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil || metricFamily.GetType() == dto.MetricType_SUMMARY && dtoMetric.Summary == nil || + metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil || metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil { return fmt.Errorf( - "collected metric %q is not a %s", - dtoMetric, metricFamily.Type, + "collected metric %s %s is not a %s", + metricFamily.GetName(), dtoMetric, metricFamily.GetType(), ) } @@ -531,19 +531,24 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d h := fnv.New64a() var buf bytes.Buffer buf.WriteString(metricFamily.GetName()) - buf.WriteByte(model.SeparatorByte) + buf.WriteByte(separatorByte) h.Write(buf.Bytes()) + // Make sure label pairs are sorted. We depend on it for the consistency + // check. Label pairs must be sorted by contract. But the point of this + // method is to check for contract violations. So we better do the sort + // now. + sort.Sort(LabelPairSorter(dtoMetric.Label)) for _, lp := range dtoMetric.Label { buf.Reset() buf.WriteString(lp.GetValue()) - buf.WriteByte(model.SeparatorByte) + buf.WriteByte(separatorByte) h.Write(buf.Bytes()) } metricHash := h.Sum64() if _, exists := metricHashes[metricHash]; exists { return fmt.Errorf( - "collected metric %q was collected before with the same name and label values", - dtoMetric, + "collected metric %s %s was collected before with the same name and label values", + metricFamily.GetName(), dtoMetric, ) } metricHashes[metricHash] = struct{}{} @@ -555,14 +560,14 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d // Desc consistency with metric family. if metricFamily.GetName() != desc.fqName { return fmt.Errorf( - "collected metric %q has name %q but should have %q", - dtoMetric, metricFamily.GetName(), desc.fqName, + "collected metric %s %s has name %q but should have %q", + metricFamily.GetName(), dtoMetric, metricFamily.GetName(), desc.fqName, ) } if metricFamily.GetHelp() != desc.help { return fmt.Errorf( - "collected metric %q has help %q but should have %q", - dtoMetric, metricFamily.GetHelp(), desc.help, + "collected metric %s %s has help %q but should have %q", + metricFamily.GetName(), dtoMetric, metricFamily.GetHelp(), desc.help, ) } @@ -576,8 +581,8 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d } if len(lpsFromDesc) != len(dtoMetric.Label) { return fmt.Errorf( - "labels in collected metric %q are inconsistent with descriptor %s", - dtoMetric, desc, + "labels in collected metric %s %s are inconsistent with descriptor %s", + metricFamily.GetName(), dtoMetric, desc, ) } sort.Sort(LabelPairSorter(lpsFromDesc)) @@ -586,8 +591,8 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d if lpFromDesc.GetName() != lpFromMetric.GetName() || lpFromDesc.Value != nil && lpFromDesc.GetValue() != lpFromMetric.GetValue() { return fmt.Errorf( - "labels in collected metric %q are inconsistent with descriptor %s", - dtoMetric, desc, + "labels in collected metric %s %s are inconsistent with descriptor %s", + metricFamily.GetName(), dtoMetric, desc, ) } } @@ -597,7 +602,10 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d // Is the desc registered? if _, exist := r.descIDs[desc.id]; !exist { - return fmt.Errorf("collected metric %q with unregistered descriptor %s", dtoMetric, desc) + return fmt.Errorf( + "collected metric %s %s with unregistered descriptor %s", + metricFamily.GetName(), dtoMetric, desc, + ) } return nil @@ -672,34 +680,6 @@ func newDefaultRegistry() *registry { return r } -func chooseEncoder(req *http.Request) (encoder, string) { - accepts := goautoneg.ParseAccept(req.Header.Get(acceptHeader)) - for _, accept := range accepts { - switch { - case accept.Type == "application" && - accept.SubType == "vnd.google.protobuf" && - accept.Params["proto"] == "io.prometheus.client.MetricFamily": - switch accept.Params["encoding"] { - case "delimited": - return text.WriteProtoDelimited, DelimitedTelemetryContentType - case "text": - return text.WriteProtoText, ProtoTextTelemetryContentType - case "compact-text": - return text.WriteProtoCompactText, ProtoCompactTextTelemetryContentType - default: - continue - } - case accept.Type == "text" && - accept.SubType == "plain" && - (accept.Params["version"] == "0.0.4" || accept.Params["version"] == ""): - return text.MetricFamilyToText, TextTelemetryContentType - default: - continue - } - } - return text.MetricFamilyToText, TextTelemetryContentType -} - // decorateWriter wraps a writer to handle gzip compression if requested. It // returns the decorated writer and the appropriate "Content-Encoding" header // (which is empty if no compression is enabled). diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go index bbf11f69778..f30c90c06be 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go @@ -68,14 +68,14 @@ func testHandler(t testing.TB) { Metric: []*dto.Metric{ { Label: []*dto.LabelPair{ - { - Name: proto.String("externallabelname"), - Value: proto.String("externalval1"), - }, { Name: proto.String("externalconstname"), Value: proto.String("externalconstvalue"), }, + { + Name: proto.String("externallabelname"), + Value: proto.String("externalval1"), + }, }, Counter: &dto.Counter{ Value: proto.Float64(1), @@ -100,27 +100,27 @@ func testHandler(t testing.TB) { externalMetricFamilyAsBytes := externalBuf.Bytes() externalMetricFamilyAsText := []byte(`# HELP externalname externaldocstring # TYPE externalname counter -externalname{externallabelname="externalval1",externalconstname="externalconstvalue"} 1 +externalname{externalconstname="externalconstvalue",externallabelname="externalval1"} 1 `) externalMetricFamilyAsProtoText := []byte(`name: "externalname" help: "externaldocstring" type: COUNTER metric: < - label: < - name: "externallabelname" - value: "externalval1" - > label: < name: "externalconstname" value: "externalconstvalue" > + label: < + name: "externallabelname" + value: "externalval1" + > counter: < value: 1 > > `) - externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric: label: counter: > + externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric: label: counter: > `) expectedMetricFamily := &dto.MetricFamily{ diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go index dd336c51994..fe81e004f67 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go @@ -25,10 +25,12 @@ import ( "github.com/golang/protobuf/proto" dto "github.com/prometheus/client_model/go" - - "github.com/prometheus/client_golang/model" ) +// quantileLabel is used for the label that defines the quantile in a +// summary. +const quantileLabel = "quantile" + // A Summary captures individual observations from an event or sample stream and // summarizes them in a manner similar to traditional summary statistics: 1. sum // of observations, 2. observation count, 3. rank estimations. @@ -57,7 +59,7 @@ var ( DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001} errQuantileLabelNotAllowed = fmt.Errorf( - "%q is not allowed as label name in summaries", model.QuantileLabel, + "%q is not allowed as label name in summaries", quantileLabel, ) ) @@ -112,7 +114,9 @@ type SummaryOpts struct { ConstLabels Labels // Objectives defines the quantile rank estimates with their respective - // absolute error. The default value is DefObjectives. + // absolute error. If Objectives[q] = e, then the value reported + // for q will be the φ-quantile value for some φ between q-e and q+e. + // The default value is DefObjectives. Objectives map[float64]float64 // MaxAge defines the duration for which an observation stays relevant @@ -170,12 +174,12 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { } for _, n := range desc.variableLabels { - if n == model.QuantileLabel { + if n == quantileLabel { panic(errQuantileLabelNotAllowed) } } for _, lp := range desc.constLabelPairs { - if lp.GetName() == model.QuantileLabel { + if lp.GetName() == quantileLabel { panic(errQuantileLabelNotAllowed) } } @@ -448,3 +452,89 @@ func (m *SummaryVec) WithLabelValues(lvs ...string) Summary { func (m *SummaryVec) With(labels Labels) Summary { return m.MetricVec.With(labels).(Summary) } + +type constSummary struct { + desc *Desc + count uint64 + sum float64 + quantiles map[float64]float64 + labelPairs []*dto.LabelPair +} + +func (s *constSummary) Desc() *Desc { + return s.desc +} + +func (s *constSummary) Write(out *dto.Metric) error { + sum := &dto.Summary{} + qs := make([]*dto.Quantile, 0, len(s.quantiles)) + + sum.SampleCount = proto.Uint64(s.count) + sum.SampleSum = proto.Float64(s.sum) + + for rank, q := range s.quantiles { + qs = append(qs, &dto.Quantile{ + Quantile: proto.Float64(rank), + Value: proto.Float64(q), + }) + } + + if len(qs) > 0 { + sort.Sort(quantSort(qs)) + } + sum.Quantile = qs + + out.Summary = sum + out.Label = s.labelPairs + + return nil +} + +// NewConstSummary returns a metric representing a Prometheus summary with fixed +// values for the count, sum, and quantiles. As those parameters cannot be +// changed, the returned value does not implement the Summary interface (but +// only the Metric interface). Users of this package will not have much use for +// it in regular operations. However, when implementing custom Collectors, it is +// useful as a throw-away metric that is generated on the fly to send it to +// Prometheus in the Collect method. +// +// quantiles maps ranks to quantile values. For example, a median latency of +// 0.23s and a 99th percentile latency of 0.56s would be expressed as: +// map[float64]float64{0.5: 0.23, 0.99: 0.56} +// +// NewConstSummary returns an error if the length of labelValues is not +// consistent with the variable labels in Desc. +func NewConstSummary( + desc *Desc, + count uint64, + sum float64, + quantiles map[float64]float64, + labelValues ...string, +) (Metric, error) { + if len(desc.variableLabels) != len(labelValues) { + return nil, errInconsistentCardinality + } + return &constSummary{ + desc: desc, + count: count, + sum: sum, + quantiles: quantiles, + labelPairs: makeLabelPairs(desc, labelValues), + }, nil +} + +// MustNewConstSummary is a version of NewConstSummary that panics where +// NewConstMetric would have returned an error. +func MustNewConstSummary( + desc *Desc, + count uint64, + sum float64, + quantiles map[float64]float64, + labelValues ...string, +) Metric { + m, err := NewConstSummary(desc, count, sum, quantiles, labelValues...) + if err != nil { + panic(err) + } + return m +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go index a5a4e77b14b..c65ab1c5312 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go @@ -21,7 +21,7 @@ import "hash/fnv" // An Untyped metric works the same as a Gauge. The only difference is that to // no type information is implied. // -// To create Gauge instances, use NewUntyped. +// To create Untyped instances, use NewUntyped. type Untyped interface { Metric Collector diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go index 107d43e3721..b54ac11e888 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go @@ -43,11 +43,15 @@ var errInconsistentCardinality = errors.New("inconsistent label cardinality") // ValueType. This is a low-level building block used by the library to back the // implementations of Counter, Gauge, and Untyped. type value struct { + // valBits containst the bits of the represented float64 value. It has + // to go first in the struct to guarantee alignment for atomic + // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG + valBits uint64 + SelfCollector desc *Desc valType ValueType - valBits uint64 // These are the bits of the represented float64 value. labelPairs []*dto.LabelPair } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go index aa49deba1b4..a1f3bdf37d8 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go +++ b/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go @@ -58,6 +58,11 @@ func (m *MetricVec) Collect(ch chan<- Metric) { // GetMetricWithLabelValues returns the Metric for the given slice of label // values (same order as the VariableLabels in Desc). If that combination of // label values is accessed for the first time, a new Metric is created. +// +// It is possible to call this method without using the returned Metric to only +// create the new Metric but leave it at its start value (e.g. a Summary or +// Histogram without any observations). See also the SummaryVec example. +// // Keeping the Metric for later use is possible (and should be considered if // performance is critical), but keep in mind that Reset, DeleteLabelValues and // Delete can be used to delete the Metric from the MetricVec. In that case, the @@ -87,8 +92,9 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { // GetMetricWith returns the Metric for the given Labels map (the label names // must match those of the VariableLabels in Desc). If that label map is -// accessed for the first time, a new Metric is created. Implications of keeping -// the Metric are the same as for GetMetricWithLabelValues. +// accessed for the first time, a new Metric is created. Implications of +// creating a Metric without using it and keeping the Metric for later use are +// the same as for GetMetricWithLabelValues. // // An error is returned if the number and names of the Labels are inconsistent // with those of the VariableLabels in Desc. diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/proto.go b/Godeps/_workspace/src/github.com/prometheus/client_golang/text/proto.go deleted file mode 100644 index e82bbb3b408..00000000000 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/proto.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 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 text - -import ( - "fmt" - "io" - - "github.com/golang/protobuf/proto" - "github.com/matttproud/golang_protobuf_extensions/pbutil" - - dto "github.com/prometheus/client_model/go" -) - -// WriteProtoDelimited writes the MetricFamily to the writer in delimited -// protobuf format and returns the number of bytes written and any error -// encountered. -func WriteProtoDelimited(w io.Writer, p *dto.MetricFamily) (int, error) { - return pbutil.WriteDelimited(w, p) -} - -// WriteProtoText writes the MetricFamily to the writer in text format and -// returns the number of bytes written and any error encountered. -func WriteProtoText(w io.Writer, p *dto.MetricFamily) (int, error) { - return fmt.Fprintf(w, "%s\n", proto.MarshalTextString(p)) -} - -// WriteProtoCompactText writes the MetricFamily to the writer in compact text -// format and returns the number of bytes written and any error encountered. -func WriteProtoCompactText(w io.Writer, p *dto.MetricFamily) (int, error) { - return fmt.Fprintf(w, "%s\n", p) -} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/protobuf b/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/protobuf deleted file mode 100644 index df48256390c..00000000000 Binary files a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/protobuf and /dev/null differ diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/protobuf.gz b/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/protobuf.gz deleted file mode 100644 index 2c8704d69e0..00000000000 Binary files a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/protobuf.gz and /dev/null differ diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/text.gz b/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/text.gz deleted file mode 100644 index 46de5995ad7..00000000000 Binary files a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/text.gz and /dev/null differ diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/bench_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go similarity index 99% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/text/bench_test.go rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go index a97409ed3f9..92b16a028aa 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/bench_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package text +package expfmt import ( "bytes" @@ -19,11 +19,14 @@ import ( "io" "io/ioutil" "testing" - dto "github.com/prometheus/client_model/go" "github.com/matttproud/golang_protobuf_extensions/pbutil" + + dto "github.com/prometheus/client_model/go" ) +var parser TextParser + // Benchmarks to show how much penalty text format parsing actually inflicts. // // Example results on Linux 3.13.0, Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz, go1.4. diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode.go new file mode 100644 index 00000000000..d36f0ecebcb --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode.go @@ -0,0 +1,410 @@ +// Copyright 2015 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 expfmt + +import ( + "fmt" + "io" + "math" + "mime" + "net/http" + + dto "github.com/prometheus/client_model/go" + + "github.com/matttproud/golang_protobuf_extensions/pbutil" + "github.com/prometheus/common/model" +) + +// Decoder types decode an input stream into metric families. +type Decoder interface { + Decode(*dto.MetricFamily) error +} + +type DecodeOptions struct { + // Timestamp is added to each value from the stream that has no explicit timestamp set. + Timestamp model.Time +} + +// ResponseFormat extracts the correct format from a HTTP response header. +func ResponseFormat(h http.Header) (Format, error) { + ct := h.Get(hdrContentType) + + mediatype, params, err := mime.ParseMediaType(ct) + if err != nil { + return "", fmt.Errorf("invalid Content-Type header %q: %s", ct, err) + } + + const ( + textType = "text/plain" + jsonType = "application/json" + ) + + switch mediatype { + case ProtoType: + if p := params["proto"]; p != ProtoProtocol { + return "", fmt.Errorf("unrecognized protocol message %s", p) + } + if e := params["encoding"]; e != "delimited" { + return "", fmt.Errorf("unsupported encoding %s", e) + } + return FmtProtoDelim, nil + + case textType: + if v, ok := params["version"]; ok && v != TextVersion { + return "", fmt.Errorf("unrecognized protocol version %s", v) + } + return FmtText, nil + + case jsonType: + var prometheusAPIVersion string + + if params["schema"] == "prometheus/telemetry" && params["version"] != "" { + prometheusAPIVersion = params["version"] + } else { + prometheusAPIVersion = h.Get("X-Prometheus-API-Version") + } + + switch prometheusAPIVersion { + case "0.0.2": + return FmtJSON2, nil + default: + return "", fmt.Errorf("unrecognized API version %s", prometheusAPIVersion) + } + } + + return "", fmt.Errorf("unsupported media type %q, expected %q or %q", mediatype, ProtoType, textType) +} + +// NewDecoder returns a new decoder based on the HTTP header. +func NewDecoder(r io.Reader, format Format) (Decoder, error) { + switch format { + case FmtProtoDelim: + return &protoDecoder{r: r}, nil + case FmtText: + return &textDecoder{r: r}, nil + case FmtJSON2: + return newJSON2Decoder(r), nil + } + return nil, fmt.Errorf("unsupported decoding format %q", format) +} + +// protoDecoder implements the Decoder interface for protocol buffers. +type protoDecoder struct { + r io.Reader +} + +// Decode implements the Decoder interface. +func (d *protoDecoder) Decode(v *dto.MetricFamily) error { + _, err := pbutil.ReadDelimited(d.r, v) + return err +} + +// textDecoder implements the Decoder interface for the text protcol. +type textDecoder struct { + r io.Reader + p TextParser + fams []*dto.MetricFamily +} + +// Decode implements the Decoder interface. +func (d *textDecoder) Decode(v *dto.MetricFamily) error { + // TODO(fabxc): Wrap this as a line reader to make streaming safer. + if len(d.fams) == 0 { + // No cached metric families, read everything and parse metrics. + fams, err := d.p.TextToMetricFamilies(d.r) + if err != nil { + return err + } + if len(fams) == 0 { + return io.EOF + } + for _, f := range fams { + d.fams = append(d.fams, f) + } + } + + *v = *d.fams[len(d.fams)-1] + d.fams = d.fams[:len(d.fams)-1] + + return nil +} + +type SampleDecoder struct { + Dec Decoder + Opts *DecodeOptions + + f dto.MetricFamily +} + +func (sd *SampleDecoder) Decode(s *model.Vector) error { + if err := sd.Dec.Decode(&sd.f); err != nil { + return err + } + *s = extractSamples(&sd.f, sd.Opts) + return nil +} + +// Extract samples builds a slice of samples from the provided metric families. +func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) model.Vector { + var all model.Vector + for _, f := range fams { + all = append(all, extractSamples(f, o)...) + } + return all +} + +func extractSamples(f *dto.MetricFamily, o *DecodeOptions) model.Vector { + switch f.GetType() { + case dto.MetricType_COUNTER: + return extractCounter(o, f) + case dto.MetricType_GAUGE: + return extractGauge(o, f) + case dto.MetricType_SUMMARY: + return extractSummary(o, f) + case dto.MetricType_UNTYPED: + return extractUntyped(o, f) + case dto.MetricType_HISTOGRAM: + return extractHistogram(o, f) + } + panic("expfmt.extractSamples: unknown metric family type") +} + +func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector { + samples := make(model.Vector, 0, len(f.Metric)) + + for _, m := range f.Metric { + if m.Counter == nil { + continue + } + + lset := make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) + + smpl := &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Counter.GetValue()), + } + + if m.TimestampMs != nil { + smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) + } else { + smpl.Timestamp = o.Timestamp + } + + samples = append(samples, smpl) + } + + return samples +} + +func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector { + samples := make(model.Vector, 0, len(f.Metric)) + + for _, m := range f.Metric { + if m.Gauge == nil { + continue + } + + lset := make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) + + smpl := &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Gauge.GetValue()), + } + + if m.TimestampMs != nil { + smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) + } else { + smpl.Timestamp = o.Timestamp + } + + samples = append(samples, smpl) + } + + return samples +} + +func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector { + samples := make(model.Vector, 0, len(f.Metric)) + + for _, m := range f.Metric { + if m.Untyped == nil { + continue + } + + lset := make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) + + smpl := &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Untyped.GetValue()), + } + + if m.TimestampMs != nil { + smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) + } else { + smpl.Timestamp = o.Timestamp + } + + samples = append(samples, smpl) + } + + return samples +} + +func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector { + samples := make(model.Vector, 0, len(f.Metric)) + + for _, m := range f.Metric { + if m.Summary == nil { + continue + } + + timestamp := o.Timestamp + if m.TimestampMs != nil { + timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) + } + + for _, q := range m.Summary.Quantile { + lset := make(model.LabelSet, len(m.Label)+2) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + // BUG(matt): Update other names to "quantile". + lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile())) + lset[model.MetricNameLabel] = model.LabelValue(f.GetName()) + + samples = append(samples, &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(q.GetValue()), + Timestamp: timestamp, + }) + } + + lset := make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") + + samples = append(samples, &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Summary.GetSampleSum()), + Timestamp: timestamp, + }) + + lset = make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") + + samples = append(samples, &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Summary.GetSampleCount()), + Timestamp: timestamp, + }) + } + + return samples +} + +func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector { + samples := make(model.Vector, 0, len(f.Metric)) + + for _, m := range f.Metric { + if m.Histogram == nil { + continue + } + + timestamp := o.Timestamp + if m.TimestampMs != nil { + timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000) + } + + infSeen := false + + for _, q := range m.Histogram.Bucket { + lset := make(model.LabelSet, len(m.Label)+2) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound())) + lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") + + if math.IsInf(q.GetUpperBound(), +1) { + infSeen = true + } + + samples = append(samples, &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(q.GetCumulativeCount()), + Timestamp: timestamp, + }) + } + + lset := make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") + + samples = append(samples, &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Histogram.GetSampleSum()), + Timestamp: timestamp, + }) + + lset = make(model.LabelSet, len(m.Label)+1) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") + + count := &model.Sample{ + Metric: model.Metric(lset), + Value: model.SampleValue(m.Histogram.GetSampleCount()), + Timestamp: timestamp, + } + samples = append(samples, count) + + if !infSeen { + // Append an infinity bucket sample. + lset := make(model.LabelSet, len(m.Label)+2) + for _, p := range m.Label { + lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf") + lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") + + samples = append(samples, &model.Sample{ + Metric: model.Metric(lset), + Value: count.Value, + Timestamp: timestamp, + }) + } + } + + return samples +} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go new file mode 100644 index 00000000000..307fcd21fed --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go @@ -0,0 +1,373 @@ +// Copyright 2015 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 expfmt + +import ( + "errors" + "io" + "net/http" + "reflect" + "sort" + "strings" + "testing" + + "github.com/prometheus/common/model" +) + +func TestTextDecoder(t *testing.T) { + 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 = model.Vector{ + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "mf1", + "label": "value1", + }, + Value: -3.14, + Timestamp: 123456, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "mf1", + "label": "value2", + }, + Value: 42, + Timestamp: ts, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "mf2", + }, + Value: 3, + Timestamp: ts, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "mf2", + }, + Value: 4, + Timestamp: ts, + }, + } + ) + + dec := &SampleDecoder{ + Dec: &textDecoder{r: strings.NewReader(in)}, + Opts: &DecodeOptions{ + Timestamp: ts, + }, + } + var all model.Vector + for { + var smpls model.Vector + err := dec.Decode(&smpls) + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + all = append(all, smpls...) + } + sort.Sort(all) + sort.Sort(out) + if !reflect.DeepEqual(all, out) { + t.Fatalf("output does not match") + } +} + +func TestProtoDecoder(t *testing.T) { + + var testTime = model.Now() + + scenarios := []struct { + in string + expected model.Vector + }{ + { + in: "", + }, + { + in: "\x8f\x01\n\rrequest_count\x12\x12Number of requests\x18\x00\"0\n#\n\x0fsome_label_name\x12\x10some_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00E\xc0\"6\n)\n\x12another_label_name\x12\x13another_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00U@", + expected: model.Vector{ + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count", + "some_label_name": "some_label_value", + }, + Value: -42, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count", + "another_label_name": "another_label_value", + }, + Value: 84, + Timestamp: testTime, + }, + }, + }, + { + in: "\xb9\x01\n\rrequest_count\x12\x12Number of requests\x18\x02\"O\n#\n\x0fsome_label_name\x12\x10some_label_value\"(\x1a\x12\t\xaeG\xe1z\x14\xae\xef?\x11\x00\x00\x00\x00\x00\x00E\xc0\x1a\x12\t+\x87\x16\xd9\xce\xf7\xef?\x11\x00\x00\x00\x00\x00\x00U\xc0\"A\n)\n\x12another_label_name\x12\x13another_label_value\"\x14\x1a\x12\t\x00\x00\x00\x00\x00\x00\xe0?\x11\x00\x00\x00\x00\x00\x00$@", + expected: model.Vector{ + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count_count", + "some_label_name": "some_label_value", + }, + Value: 0, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count_sum", + "some_label_name": "some_label_value", + }, + Value: 0, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count", + "some_label_name": "some_label_value", + "quantile": "0.99", + }, + Value: -42, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count", + "some_label_name": "some_label_value", + "quantile": "0.999", + }, + Value: -84, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count_count", + "another_label_name": "another_label_value", + }, + Value: 0, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count_sum", + "another_label_name": "another_label_value", + }, + Value: 0, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count", + "another_label_name": "another_label_value", + "quantile": "0.5", + }, + Value: 10, + Timestamp: testTime, + }, + }, + }, + { + in: "\x8d\x01\n\x1drequest_duration_microseconds\x12\x15The response latency.\x18\x04\"S:Q\b\x85\x15\x11\xcd\xcc\xccL\x8f\xcb:A\x1a\v\b{\x11\x00\x00\x00\x00\x00\x00Y@\x1a\f\b\x9c\x03\x11\x00\x00\x00\x00\x00\x00^@\x1a\f\b\xd0\x04\x11\x00\x00\x00\x00\x00\x00b@\x1a\f\b\xf4\v\x11\x9a\x99\x99\x99\x99\x99e@\x1a\f\b\x85\x15\x11\x00\x00\x00\x00\x00\x00\xf0\u007f", + expected: model.Vector{ + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_bucket", + "le": "100", + }, + Value: 123, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_bucket", + "le": "120", + }, + Value: 412, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_bucket", + "le": "144", + }, + Value: 592, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_bucket", + "le": "172.8", + }, + Value: 1524, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_bucket", + "le": "+Inf", + }, + Value: 2693, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_sum", + }, + Value: 1756047.3, + Timestamp: testTime, + }, + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_duration_microseconds_count", + }, + Value: 2693, + Timestamp: testTime, + }, + }, + }, + { + // The metric type is unset in this protobuf, which needs to be handled + // correctly by the decoder. + in: "\x1c\n\rrequest_count\"\v\x1a\t\t\x00\x00\x00\x00\x00\x00\xf0?", + expected: model.Vector{ + &model.Sample{ + Metric: model.Metric{ + model.MetricNameLabel: "request_count", + }, + Value: 1, + Timestamp: testTime, + }, + }, + }, + } + + for i, scenario := range scenarios { + dec := &SampleDecoder{ + Dec: &protoDecoder{r: strings.NewReader(scenario.in)}, + Opts: &DecodeOptions{ + Timestamp: testTime, + }, + } + + var all model.Vector + for { + var smpls model.Vector + err := dec.Decode(&smpls) + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + all = append(all, smpls...) + } + sort.Sort(all) + sort.Sort(scenario.expected) + if !reflect.DeepEqual(all, scenario.expected) { + t.Fatalf("%d. output does not match, want: %#v, got %#v", i, scenario.expected, all) + } + } +} + +func testDiscriminatorHTTPHeader(t testing.TB) { + var scenarios = []struct { + input map[string]string + output Format + err error + }{ + { + input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`}, + output: FmtProtoDelim, + err: nil, + }, + { + input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="illegal"; encoding="delimited"`}, + output: "", + err: errors.New("unrecognized protocol message illegal"), + }, + { + input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="illegal"`}, + output: "", + err: errors.New("unsupported encoding illegal"), + }, + { + input: map[string]string{"Content-Type": `text/plain; version=0.0.4`}, + output: FmtText, + err: nil, + }, + { + input: map[string]string{"Content-Type": `text/plain`}, + output: FmtText, + err: nil, + }, + { + input: map[string]string{"Content-Type": `text/plain; version=0.0.3`}, + output: "", + err: errors.New("unrecognized protocol version 0.0.3"), + }, + } + + for i, scenario := range scenarios { + var header http.Header + + if len(scenario.input) > 0 { + header = http.Header{} + } + + for key, value := range scenario.input { + header.Add(key, value) + } + + actual, err := ResponseFormat(header) + + if scenario.err != err { + if scenario.err != nil && err != nil { + if scenario.err.Error() != err.Error() { + t.Errorf("%d. expected %s, got %s", i, scenario.err, err) + } + } else if scenario.err != nil || err != nil { + t.Errorf("%d. expected %s, got %s", i, scenario.err, err) + } + } + + if !reflect.DeepEqual(scenario.output, actual) { + t.Errorf("%d. expected %s, got %s", i, scenario.output, actual) + } + } +} + +func TestDiscriminatorHTTPHeader(t *testing.T) { + testDiscriminatorHTTPHeader(t) +} + +func BenchmarkDiscriminatorHTTPHeader(b *testing.B) { + for i := 0; i < b.N; i++ { + testDiscriminatorHTTPHeader(b) + } +} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/encode.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/encode.go new file mode 100644 index 00000000000..392ca90ee25 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/encode.go @@ -0,0 +1,88 @@ +// Copyright 2015 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 expfmt + +import ( + "fmt" + "io" + "net/http" + + "bitbucket.org/ww/goautoneg" + "github.com/golang/protobuf/proto" + "github.com/matttproud/golang_protobuf_extensions/pbutil" + + dto "github.com/prometheus/client_model/go" +) + +// Encoder types encode metric families into an underlying wire protocol. +type Encoder interface { + Encode(*dto.MetricFamily) error +} + +type encoder func(*dto.MetricFamily) error + +func (e encoder) Encode(v *dto.MetricFamily) error { + return e(v) +} + +// Negotiate returns the Content-Type based on the given Accept header. +// If no appropriate accepted type is found, FmtText is returned. +func Negotiate(h http.Header) Format { + for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) { + // Check for protocol buffer + if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol { + switch ac.Params["encoding"] { + case "delimited": + return FmtProtoDelim + case "text": + return FmtProtoText + case "compact-text": + return FmtProtoCompact + } + } + // Check for text format. + ver := ac.Params["version"] + if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") { + return FmtText + } + } + return FmtText +} + +// NewEncoder returns a new encoder based on content type negotiation. +func NewEncoder(w io.Writer, format Format) Encoder { + switch format { + case FmtProtoDelim: + return encoder(func(v *dto.MetricFamily) error { + _, err := pbutil.WriteDelimited(w, v) + return err + }) + case FmtProtoCompact: + return encoder(func(v *dto.MetricFamily) error { + _, err := fmt.Fprintln(w, v.String()) + return err + }) + case FmtProtoText: + return encoder(func(v *dto.MetricFamily) error { + _, err := fmt.Fprintln(w, proto.MarshalTextString(v)) + return err + }) + case FmtText: + return encoder(func(v *dto.MetricFamily) error { + _, err := MetricFamilyToText(w, v) + return err + }) + } + panic("expfmt.NewEncoder: unknown format") +} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/expfmt.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/expfmt.go new file mode 100644 index 00000000000..3637dd7661d --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/expfmt.go @@ -0,0 +1,37 @@ +// Copyright 2015 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. + +// A package for reading and writing Prometheus metrics. +package expfmt + +type Format string + +const ( + TextVersion = "0.0.4" + + ProtoType = `application/vnd.google.protobuf` + ProtoProtocol = `io.prometheus.client.MetricFamily` + ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";" + + // The Content-Type values for the different wire protocols. + FmtText Format = `text/plain; version=` + TextVersion + FmtProtoDelim Format = ProtoFmt + ` encoding=delimited` + FmtProtoText Format = ProtoFmt + ` encoding=text` + FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text` + FmtJSON2 Format = `application/json; version=0.0.2` +) + +const ( + hdrContentType = "Content-Type" + hdrAccept = "Accept" +) diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelvalue.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz.go similarity index 51% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelvalue.go rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz.go index df2d14cc122..14f92014692 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelvalue.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz.go @@ -1,4 +1,4 @@ -// Copyright 2013 The Prometheus Authors +// Copyright 2014 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 @@ -11,26 +11,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package model +// Build only when actually fuzzing +// +build gofuzz -import ( - "sort" -) +package expfmt -// A LabelValue is an associated value for a LabelName. -type LabelValue string +import "bytes" -// LabelValues is a sortable LabelValue slice. It implements sort.Interface. -type LabelValues []LabelValue +// Fuzz text metric parser with with github.com/dvyukov/go-fuzz: +// +// go-fuzz-build github.com/prometheus/client_golang/text +// go-fuzz -bin text-fuzz.zip -workdir fuzz +// +// Further input samples should go in the folder fuzz/corpus. +func Fuzz(in []byte) int { + parser := TextParser{} + _, err := parser.TextToMetricFamilies(bytes.NewReader(in)) -func (l LabelValues) Len() int { - return len(l) -} - -func (l LabelValues) Less(i, j int) bool { - return sort.StringsAreSorted([]string{string(l[i]), string(l[j])}) -} - -func (l LabelValues) Swap(i, j int) { - l[i], l[j] = l[j], l[i] + if err != nil { + return 0 + } + + return 1 } diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 new file mode 100644 index 00000000000..139597f9cb0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 @@ -0,0 +1,2 @@ + + diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 new file mode 100644 index 00000000000..2ae8706797b --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 @@ -0,0 +1,6 @@ + +minimal_metric 1.234 +another_metric -3e3 103948 +# Even that: +no_labels{} 3 +# HELP line for non-existing metric will be ignored. diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 new file mode 100644 index 00000000000..5c351db36d3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 @@ -0,0 +1,12 @@ + +# A normal comment. +# +# TYPE name counter +name{labelname="val1",basename="basevalue"} NaN +name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890 +# HELP name two-line\n doc str\\ing + + # HELP name2 doc str"ing 2 + # TYPE name2 gauge +name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321 +name2{ labelname = "val1" , }-Inf diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 new file mode 100644 index 00000000000..0b3c345aa9c --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 @@ -0,0 +1,22 @@ + +# TYPE my_summary summary +my_summary{n1="val1",quantile="0.5"} 110 +decoy -1 -2 +my_summary{n1="val1",quantile="0.9"} 140 1 +my_summary_count{n1="val1"} 42 +# Latest timestamp wins in case of a summary. +my_summary_sum{n1="val1"} 4711 2 +fake_sum{n1="val1"} 2001 +# TYPE another_summary summary +another_summary_count{n2="val2",n1="val1"} 20 +my_summary_count{n2="val2",n1="val1"} 5 5 +another_summary{n1="val1",n2="val2",quantile=".3"} -1.2 +my_summary_sum{n1="val2"} 08 15 +my_summary{n1="val3", quantile="0.2"} 4711 + my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN +# some +# funny comments +# HELP +# HELP +# HELP my_summary +# HELP my_summary diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 new file mode 100644 index 00000000000..bde0a387aa2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 @@ -0,0 +1,10 @@ + +# HELP request_duration_microseconds The response latency. +# TYPE request_duration_microseconds histogram +request_duration_microseconds_bucket{le="100"} 123 +request_duration_microseconds_bucket{le="120"} 412 +request_duration_microseconds_bucket{le="144"} 592 +request_duration_microseconds_bucket{le="172.8"} 1524 +request_duration_microseconds_bucket{le="+Inf"} 2693 +request_duration_microseconds_sum 1.7560473e+06 +request_duration_microseconds_count 2693 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 new file mode 100644 index 00000000000..4c67f9a198a --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 @@ -0,0 +1 @@ +bla 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 new file mode 100644 index 00000000000..b853478ee2f --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 @@ -0,0 +1 @@ +metric{label="\t"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 new file mode 100644 index 00000000000..b5fe5f5a68c --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 @@ -0,0 +1 @@ +metric{label="bla"} 3.14 2 3 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 new file mode 100644 index 00000000000..57c7fbc0bc4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 @@ -0,0 +1 @@ +metric{label="bla"} blubb diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 new file mode 100644 index 00000000000..0a9df79a1e6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 @@ -0,0 +1,3 @@ + +# HELP metric one +# HELP metric two diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 new file mode 100644 index 00000000000..5bc7427813e --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 @@ -0,0 +1,3 @@ + +# TYPE metric counter +# TYPE metric untyped diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 new file mode 100644 index 00000000000..a9a24265b29 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 @@ -0,0 +1,3 @@ + +metric 4.12 +# TYPE metric counter diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 new file mode 100644 index 00000000000..7e95ca8f4c1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 @@ -0,0 +1,2 @@ + +# TYPE metric bla diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 new file mode 100644 index 00000000000..7825f888723 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 @@ -0,0 +1,2 @@ + +# TYPE met-ric diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 new file mode 100644 index 00000000000..8f35cae0cf0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 @@ -0,0 +1 @@ +@invalidmetric{label="bla"} 3.14 2 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 new file mode 100644 index 00000000000..7ca2cc268b0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 @@ -0,0 +1 @@ +{label="bla"} 3.14 2 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 new file mode 100644 index 00000000000..7a6ccc0dd47 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 @@ -0,0 +1,3 @@ + +# TYPE metric histogram +metric_bucket{le="bla"} 3.14 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 new file mode 100644 index 00000000000..726d0017cb0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 @@ -0,0 +1,3 @@ + +metric{label="new +line"} 3.14 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 new file mode 100644 index 00000000000..6aa9e308140 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 @@ -0,0 +1 @@ +metric{@="bla"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 new file mode 100644 index 00000000000..d112cb90252 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 @@ -0,0 +1 @@ +metric{__name__="bla"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 new file mode 100644 index 00000000000..b34554a8d6b --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 @@ -0,0 +1 @@ +metric{label+="bla"} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 new file mode 100644 index 00000000000..c4d7df3d111 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 @@ -0,0 +1 @@ +metric{label=bla} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 new file mode 100644 index 00000000000..97eafc4a653 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 @@ -0,0 +1,3 @@ + +# TYPE metric summary +metric{quantile="bla"} 3.14 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 new file mode 100644 index 00000000000..fc706496b7c --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 @@ -0,0 +1 @@ +metric{label="bla"+} 3.14 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 new file mode 100644 index 00000000000..57b4879c059 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 @@ -0,0 +1 @@ +metric{label="bla"} 3.14 2.72 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal new file mode 100644 index 00000000000..be1e6a369dd --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal @@ -0,0 +1 @@ +m{} 0 diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode.go new file mode 100644 index 00000000000..67e3a0d4d65 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode.go @@ -0,0 +1,162 @@ +// Copyright 2015 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 expfmt + +import ( + "encoding/json" + "fmt" + "io" + "sort" + + "github.com/golang/protobuf/proto" + dto "github.com/prometheus/client_model/go" + + "github.com/prometheus/common/model" +) + +type json2Decoder struct { + dec *json.Decoder + fams []*dto.MetricFamily +} + +func newJSON2Decoder(r io.Reader) Decoder { + return &json2Decoder{ + dec: json.NewDecoder(r), + } +} + +type histogram002 struct { + Labels model.LabelSet `json:"labels"` + Values map[string]float64 `json:"value"` +} + +type counter002 struct { + Labels model.LabelSet `json:"labels"` + Value float64 `json:"value"` +} + +func protoLabelSet(base, ext model.LabelSet) []*dto.LabelPair { + labels := base.Clone().Merge(ext) + delete(labels, model.MetricNameLabel) + + names := make([]string, 0, len(labels)) + for ln := range labels { + names = append(names, string(ln)) + } + sort.Strings(names) + + pairs := make([]*dto.LabelPair, 0, len(labels)) + + for _, ln := range names { + lv := labels[model.LabelName(ln)] + + pairs = append(pairs, &dto.LabelPair{ + Name: proto.String(ln), + Value: proto.String(string(lv)), + }) + } + + return pairs +} + +func (d *json2Decoder) more() error { + var entities []struct { + BaseLabels model.LabelSet `json:"baseLabels"` + Docstring string `json:"docstring"` + Metric struct { + Type string `json:"type"` + Values json.RawMessage `json:"value"` + } `json:"metric"` + } + + if err := d.dec.Decode(&entities); err != nil { + return err + } + for _, e := range entities { + f := &dto.MetricFamily{ + Name: proto.String(string(e.BaseLabels[model.MetricNameLabel])), + Help: proto.String(e.Docstring), + Type: dto.MetricType_UNTYPED.Enum(), + Metric: []*dto.Metric{}, + } + + d.fams = append(d.fams, f) + + switch e.Metric.Type { + case "counter", "gauge": + var values []counter002 + + if err := json.Unmarshal(e.Metric.Values, &values); err != nil { + return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err) + } + + for _, ctr := range values { + f.Metric = append(f.Metric, &dto.Metric{ + Label: protoLabelSet(e.BaseLabels, ctr.Labels), + Untyped: &dto.Untyped{ + Value: proto.Float64(ctr.Value), + }, + }) + } + + case "histogram": + var values []histogram002 + + if err := json.Unmarshal(e.Metric.Values, &values); err != nil { + return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err) + } + + for _, hist := range values { + quants := make([]string, 0, len(values)) + for q := range hist.Values { + quants = append(quants, q) + } + + sort.Strings(quants) + + for _, q := range quants { + value := hist.Values[q] + // The correct label is "quantile" but to not break old expressions + // this remains "percentile" + hist.Labels["percentile"] = model.LabelValue(q) + + f.Metric = append(f.Metric, &dto.Metric{ + Label: protoLabelSet(e.BaseLabels, hist.Labels), + Untyped: &dto.Untyped{ + Value: proto.Float64(value), + }, + }) + } + } + + default: + return fmt.Errorf("unknown metric type %q", e.Metric.Type) + } + } + return nil +} + +// Decode implements the Decoder interface. +func (d *json2Decoder) Decode(v *dto.MetricFamily) error { + if len(d.fams) == 0 { + if err := d.more(); err != nil { + return err + } + } + + *v = *d.fams[0] + d.fams = d.fams[1:] + + return nil +} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go new file mode 100644 index 00000000000..c98ea29e176 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go @@ -0,0 +1,124 @@ +// Copyright 2015 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 expfmt + +import ( + "os" + "reflect" + "testing" + + "github.com/golang/protobuf/proto" + dto "github.com/prometheus/client_model/go" +) + +func TestJSON2Decode(t *testing.T) { + f, err := os.Open("testdata/json2") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + dec := newJSON2Decoder(f) + + var v1 dto.MetricFamily + if err := dec.Decode(&v1); err != nil { + t.Fatal(err) + } + + exp1 := dto.MetricFamily{ + Type: dto.MetricType_UNTYPED.Enum(), + Help: proto.String("RPC calls."), + Name: proto.String("rpc_calls_total"), + Metric: []*dto.Metric{ + { + Label: []*dto.LabelPair{ + { + Name: proto.String("job"), + Value: proto.String("batch_job"), + }, { + Name: proto.String("service"), + Value: proto.String("zed"), + }, + }, + Untyped: &dto.Untyped{ + Value: proto.Float64(25), + }, + }, + { + Label: []*dto.LabelPair{ + { + Name: proto.String("job"), + Value: proto.String("batch_job"), + }, { + Name: proto.String("service"), + Value: proto.String("bar"), + }, + }, + Untyped: &dto.Untyped{ + Value: proto.Float64(24), + }, + }, + }, + } + + if !reflect.DeepEqual(v1, exp1) { + t.Fatalf("Expected %v, got %v", exp1, v1) + } + + var v2 dto.MetricFamily + if err := dec.Decode(&v2); err != nil { + t.Fatal(err) + } + + exp2 := dto.MetricFamily{ + Type: dto.MetricType_UNTYPED.Enum(), + Help: proto.String("RPC latency."), + Name: proto.String("rpc_latency_microseconds"), + Metric: []*dto.Metric{ + { + Label: []*dto.LabelPair{ + { + Name: proto.String("percentile"), + Value: proto.String("0.010000"), + }, { + Name: proto.String("service"), + Value: proto.String("foo"), + }, + }, + Untyped: &dto.Untyped{ + Value: proto.Float64(15), + }, + }, + { + Label: []*dto.LabelPair{ + { + Name: proto.String("percentile"), + Value: proto.String("0.990000"), + }, { + Name: proto.String("service"), + Value: proto.String("foo"), + }, + }, + Untyped: &dto.Untyped{ + Value: proto.Float64(17), + }, + }, + }, + } + + if !reflect.DeepEqual(v2, exp2) { + t.Fatalf("Expected %v, got %v", exp2, v2) + } + +} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 new file mode 100644 index 00000000000..b914c938659 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 @@ -0,0 +1,46 @@ +[ + { + "baseLabels": { + "__name__": "rpc_calls_total", + "job": "batch_job" + }, + "docstring": "RPC calls.", + "metric": { + "type": "counter", + "value": [ + { + "labels": { + "service": "zed" + }, + "value": 25 + }, + { + "labels": { + "service": "bar" + }, + "value": 24 + } + ] + } + }, + { + "baseLabels": { + "__name__": "rpc_latency_microseconds" + }, + "docstring": "RPC latency.", + "metric": { + "type": "histogram", + "value": [ + { + "labels": { + "service": "foo" + }, + "value": { + "0.010000": 15, + "0.990000": 17 + } + } + ] + } + } +] diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf new file mode 100644 index 00000000000..d5aae509150 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf @@ -0,0 +1,516 @@ +fc08 0a22 6874 7470 5f72 6571 7565 7374 +5f64 7572 6174 696f 6e5f 6d69 6372 6f73 +6563 6f6e 6473 122b 5468 6520 4854 5450 +2072 6571 7565 7374 206c 6174 656e 6369 +6573 2069 6e20 6d69 6372 6f73 6563 6f6e +6473 2e18 0222 570a 0c0a 0768 616e 646c +6572 1201 2f22 4708 0011 0000 0000 0000 +0000 1a12 0900 0000 0000 00e0 3f11 0000 +0000 0000 0000 1a12 09cd cccc cccc ccec +3f11 0000 0000 0000 0000 1a12 09ae 47e1 +7a14 aeef 3f11 0000 0000 0000 0000 225d +0a12 0a07 6861 6e64 6c65 7212 072f 616c +6572 7473 2247 0800 1100 0000 0000 0000 +001a 1209 0000 0000 0000 e03f 1100 0000 +0000 0000 001a 1209 cdcc cccc cccc ec3f +1100 0000 0000 0000 001a 1209 ae47 e17a +14ae ef3f 1100 0000 0000 0000 0022 620a +170a 0768 616e 646c 6572 120c 2f61 7069 +2f6d 6574 7269 6373 2247 0800 1100 0000 +0000 0000 001a 1209 0000 0000 0000 e03f +1100 0000 0000 0000 001a 1209 cdcc cccc +cccc ec3f 1100 0000 0000 0000 001a 1209 +ae47 e17a 14ae ef3f 1100 0000 0000 0000 +0022 600a 150a 0768 616e 646c 6572 120a +2f61 7069 2f71 7565 7279 2247 0800 1100 +0000 0000 0000 001a 1209 0000 0000 0000 +e03f 1100 0000 0000 0000 001a 1209 cdcc +cccc cccc ec3f 1100 0000 0000 0000 001a +1209 ae47 e17a 14ae ef3f 1100 0000 0000 +0000 0022 660a 1b0a 0768 616e 646c 6572 +1210 2f61 7069 2f71 7565 7279 5f72 616e +6765 2247 0800 1100 0000 0000 0000 001a +1209 0000 0000 0000 e03f 1100 0000 0000 +0000 001a 1209 cdcc cccc cccc ec3f 1100 +0000 0000 0000 001a 1209 ae47 e17a 14ae +ef3f 1100 0000 0000 0000 0022 620a 170a +0768 616e 646c 6572 120c 2f61 7069 2f74 +6172 6765 7473 2247 0800 1100 0000 0000 +0000 001a 1209 0000 0000 0000 e03f 1100 +0000 0000 0000 001a 1209 cdcc cccc cccc +ec3f 1100 0000 0000 0000 001a 1209 ae47 +e17a 14ae ef3f 1100 0000 0000 0000 0022 +600a 150a 0768 616e 646c 6572 120a 2f63 +6f6e 736f 6c65 732f 2247 0800 1100 0000 +0000 0000 001a 1209 0000 0000 0000 e03f +1100 0000 0000 0000 001a 1209 cdcc cccc +cccc ec3f 1100 0000 0000 0000 001a 1209 +ae47 e17a 14ae ef3f 1100 0000 0000 0000 +0022 5c0a 110a 0768 616e 646c 6572 1206 +2f67 7261 7068 2247 0800 1100 0000 0000 +0000 001a 1209 0000 0000 0000 e03f 1100 +0000 0000 0000 001a 1209 cdcc cccc cccc +ec3f 1100 0000 0000 0000 001a 1209 ae47 +e17a 14ae ef3f 1100 0000 0000 0000 0022 +5b0a 100a 0768 616e 646c 6572 1205 2f68 +6561 7022 4708 0011 0000 0000 0000 0000 +1a12 0900 0000 0000 00e0 3f11 0000 0000 +0000 0000 1a12 09cd cccc cccc ccec 3f11 +0000 0000 0000 0000 1a12 09ae 47e1 7a14 +aeef 3f11 0000 0000 0000 0000 225e 0a13 +0a07 6861 6e64 6c65 7212 082f 7374 6174 +6963 2f22 4708 0011 0000 0000 0000 0000 +1a12 0900 0000 0000 00e0 3f11 0000 0000 +0000 0000 1a12 09cd cccc cccc ccec 3f11 +0000 0000 0000 0000 1a12 09ae 47e1 7a14 +aeef 3f11 0000 0000 0000 0000 2260 0a15 +0a07 6861 6e64 6c65 7212 0a70 726f 6d65 +7468 6575 7322 4708 3b11 5b8f c2f5 083f +f440 1a12 0900 0000 0000 00e0 3f11 e17a +14ae c7af 9340 1a12 09cd cccc cccc ccec +3f11 2fdd 2406 81f0 9640 1a12 09ae 47e1 +7a14 aeef 3f11 3d0a d7a3 b095 a740 e608 +0a17 6874 7470 5f72 6571 7565 7374 5f73 +697a 655f 6279 7465 7312 2054 6865 2048 +5454 5020 7265 7175 6573 7420 7369 7a65 +7320 696e 2062 7974 6573 2e18 0222 570a +0c0a 0768 616e 646c 6572 1201 2f22 4708 +0011 0000 0000 0000 0000 1a12 0900 0000 +0000 00e0 3f11 0000 0000 0000 0000 1a12 +09cd cccc cccc ccec 3f11 0000 0000 0000 +0000 1a12 09ae 47e1 7a14 aeef 3f11 0000 +0000 0000 0000 225d 0a12 0a07 6861 6e64 +6c65 7212 072f 616c 6572 7473 2247 0800 +1100 0000 0000 0000 001a 1209 0000 0000 +0000 e03f 1100 0000 0000 0000 001a 1209 +cdcc cccc cccc ec3f 1100 0000 0000 0000 +001a 1209 ae47 e17a 14ae ef3f 1100 0000 +0000 0000 0022 620a 170a 0768 616e 646c +6572 120c 2f61 7069 2f6d 6574 7269 6373 +2247 0800 1100 0000 0000 0000 001a 1209 +0000 0000 0000 e03f 1100 0000 0000 0000 +001a 1209 cdcc cccc cccc ec3f 1100 0000 +0000 0000 001a 1209 ae47 e17a 14ae ef3f +1100 0000 0000 0000 0022 600a 150a 0768 +616e 646c 6572 120a 2f61 7069 2f71 7565 +7279 2247 0800 1100 0000 0000 0000 001a +1209 0000 0000 0000 e03f 1100 0000 0000 +0000 001a 1209 cdcc cccc cccc ec3f 1100 +0000 0000 0000 001a 1209 ae47 e17a 14ae +ef3f 1100 0000 0000 0000 0022 660a 1b0a +0768 616e 646c 6572 1210 2f61 7069 2f71 +7565 7279 5f72 616e 6765 2247 0800 1100 +0000 0000 0000 001a 1209 0000 0000 0000 +e03f 1100 0000 0000 0000 001a 1209 cdcc +cccc cccc ec3f 1100 0000 0000 0000 001a +1209 ae47 e17a 14ae ef3f 1100 0000 0000 +0000 0022 620a 170a 0768 616e 646c 6572 +120c 2f61 7069 2f74 6172 6765 7473 2247 +0800 1100 0000 0000 0000 001a 1209 0000 +0000 0000 e03f 1100 0000 0000 0000 001a +1209 cdcc cccc cccc ec3f 1100 0000 0000 +0000 001a 1209 ae47 e17a 14ae ef3f 1100 +0000 0000 0000 0022 600a 150a 0768 616e +646c 6572 120a 2f63 6f6e 736f 6c65 732f +2247 0800 1100 0000 0000 0000 001a 1209 +0000 0000 0000 e03f 1100 0000 0000 0000 +001a 1209 cdcc cccc cccc ec3f 1100 0000 +0000 0000 001a 1209 ae47 e17a 14ae ef3f +1100 0000 0000 0000 0022 5c0a 110a 0768 +616e 646c 6572 1206 2f67 7261 7068 2247 +0800 1100 0000 0000 0000 001a 1209 0000 +0000 0000 e03f 1100 0000 0000 0000 001a +1209 cdcc cccc cccc ec3f 1100 0000 0000 +0000 001a 1209 ae47 e17a 14ae ef3f 1100 +0000 0000 0000 0022 5b0a 100a 0768 616e +646c 6572 1205 2f68 6561 7022 4708 0011 +0000 0000 0000 0000 1a12 0900 0000 0000 +00e0 3f11 0000 0000 0000 0000 1a12 09cd +cccc cccc ccec 3f11 0000 0000 0000 0000 +1a12 09ae 47e1 7a14 aeef 3f11 0000 0000 +0000 0000 225e 0a13 0a07 6861 6e64 6c65 +7212 082f 7374 6174 6963 2f22 4708 0011 +0000 0000 0000 0000 1a12 0900 0000 0000 +00e0 3f11 0000 0000 0000 0000 1a12 09cd +cccc cccc ccec 3f11 0000 0000 0000 0000 +1a12 09ae 47e1 7a14 aeef 3f11 0000 0000 +0000 0000 2260 0a15 0a07 6861 6e64 6c65 +7212 0a70 726f 6d65 7468 6575 7322 4708 +3b11 0000 0000 40c4 d040 1a12 0900 0000 +0000 00e0 3f11 0000 0000 0030 7240 1a12 +09cd cccc cccc ccec 3f11 0000 0000 0030 +7240 1a12 09ae 47e1 7a14 aeef 3f11 0000 +0000 0030 7240 7c0a 1368 7474 705f 7265 +7175 6573 7473 5f74 6f74 616c 1223 546f +7461 6c20 6e75 6d62 6572 206f 6620 4854 +5450 2072 6571 7565 7374 7320 6d61 6465 +2e18 0022 3e0a 0b0a 0463 6f64 6512 0332 +3030 0a15 0a07 6861 6e64 6c65 7212 0a70 +726f 6d65 7468 6575 730a 0d0a 066d 6574 +686f 6412 0367 6574 1a09 0900 0000 0000 +804d 40e8 080a 1868 7474 705f 7265 7370 +6f6e 7365 5f73 697a 655f 6279 7465 7312 +2154 6865 2048 5454 5020 7265 7370 6f6e +7365 2073 697a 6573 2069 6e20 6279 7465 +732e 1802 2257 0a0c 0a07 6861 6e64 6c65 +7212 012f 2247 0800 1100 0000 0000 0000 +001a 1209 0000 0000 0000 e03f 1100 0000 +0000 0000 001a 1209 cdcc cccc cccc ec3f +1100 0000 0000 0000 001a 1209 ae47 e17a +14ae ef3f 1100 0000 0000 0000 0022 5d0a +120a 0768 616e 646c 6572 1207 2f61 6c65 +7274 7322 4708 0011 0000 0000 0000 0000 +1a12 0900 0000 0000 00e0 3f11 0000 0000 +0000 0000 1a12 09cd cccc cccc ccec 3f11 +0000 0000 0000 0000 1a12 09ae 47e1 7a14 +aeef 3f11 0000 0000 0000 0000 2262 0a17 +0a07 6861 6e64 6c65 7212 0c2f 6170 692f +6d65 7472 6963 7322 4708 0011 0000 0000 +0000 0000 1a12 0900 0000 0000 00e0 3f11 +0000 0000 0000 0000 1a12 09cd cccc cccc +ccec 3f11 0000 0000 0000 0000 1a12 09ae +47e1 7a14 aeef 3f11 0000 0000 0000 0000 +2260 0a15 0a07 6861 6e64 6c65 7212 0a2f +6170 692f 7175 6572 7922 4708 0011 0000 +0000 0000 0000 1a12 0900 0000 0000 00e0 +3f11 0000 0000 0000 0000 1a12 09cd cccc +cccc ccec 3f11 0000 0000 0000 0000 1a12 +09ae 47e1 7a14 aeef 3f11 0000 0000 0000 +0000 2266 0a1b 0a07 6861 6e64 6c65 7212 +102f 6170 692f 7175 6572 795f 7261 6e67 +6522 4708 0011 0000 0000 0000 0000 1a12 +0900 0000 0000 00e0 3f11 0000 0000 0000 +0000 1a12 09cd cccc cccc ccec 3f11 0000 +0000 0000 0000 1a12 09ae 47e1 7a14 aeef +3f11 0000 0000 0000 0000 2262 0a17 0a07 +6861 6e64 6c65 7212 0c2f 6170 692f 7461 +7267 6574 7322 4708 0011 0000 0000 0000 +0000 1a12 0900 0000 0000 00e0 3f11 0000 +0000 0000 0000 1a12 09cd cccc cccc ccec +3f11 0000 0000 0000 0000 1a12 09ae 47e1 +7a14 aeef 3f11 0000 0000 0000 0000 2260 +0a15 0a07 6861 6e64 6c65 7212 0a2f 636f +6e73 6f6c 6573 2f22 4708 0011 0000 0000 +0000 0000 1a12 0900 0000 0000 00e0 3f11 +0000 0000 0000 0000 1a12 09cd cccc cccc +ccec 3f11 0000 0000 0000 0000 1a12 09ae +47e1 7a14 aeef 3f11 0000 0000 0000 0000 +225c 0a11 0a07 6861 6e64 6c65 7212 062f +6772 6170 6822 4708 0011 0000 0000 0000 +0000 1a12 0900 0000 0000 00e0 3f11 0000 +0000 0000 0000 1a12 09cd cccc cccc ccec +3f11 0000 0000 0000 0000 1a12 09ae 47e1 +7a14 aeef 3f11 0000 0000 0000 0000 225b +0a10 0a07 6861 6e64 6c65 7212 052f 6865 +6170 2247 0800 1100 0000 0000 0000 001a +1209 0000 0000 0000 e03f 1100 0000 0000 +0000 001a 1209 cdcc cccc cccc ec3f 1100 +0000 0000 0000 001a 1209 ae47 e17a 14ae +ef3f 1100 0000 0000 0000 0022 5e0a 130a +0768 616e 646c 6572 1208 2f73 7461 7469 +632f 2247 0800 1100 0000 0000 0000 001a +1209 0000 0000 0000 e03f 1100 0000 0000 +0000 001a 1209 cdcc cccc cccc ec3f 1100 +0000 0000 0000 001a 1209 ae47 e17a 14ae +ef3f 1100 0000 0000 0000 0022 600a 150a +0768 616e 646c 6572 120a 7072 6f6d 6574 +6865 7573 2247 083b 1100 0000 00e0 b4fc +401a 1209 0000 0000 0000 e03f 1100 0000 +0000 349f 401a 1209 cdcc cccc cccc ec3f +1100 0000 0000 08a0 401a 1209 ae47 e17a +14ae ef3f 1100 0000 0000 0aa0 405c 0a19 +7072 6f63 6573 735f 6370 755f 7365 636f +6e64 735f 746f 7461 6c12 3054 6f74 616c +2075 7365 7220 616e 6420 7379 7374 656d +2043 5055 2074 696d 6520 7370 656e 7420 +696e 2073 6563 6f6e 6473 2e18 0022 0b1a +0909 a470 3d0a d7a3 d03f 4f0a 1270 726f +6365 7373 5f67 6f72 6f75 7469 6e65 7312 +2a4e 756d 6265 7220 6f66 2067 6f72 6f75 +7469 6e65 7320 7468 6174 2063 7572 7265 +6e74 6c79 2065 7869 7374 2e18 0122 0b12 +0909 0000 0000 0000 5140 4a0a 0f70 726f +6365 7373 5f6d 6178 5f66 6473 1228 4d61 +7869 6d75 6d20 6e75 6d62 6572 206f 6620 +6f70 656e 2066 696c 6520 6465 7363 7269 +7074 6f72 732e 1801 220b 1209 0900 0000 +0000 00c0 4043 0a10 7072 6f63 6573 735f +6f70 656e 5f66 6473 1220 4e75 6d62 6572 +206f 6620 6f70 656e 2066 696c 6520 6465 +7363 7269 7074 6f72 732e 1801 220b 1209 +0900 0000 0000 003d 404e 0a1d 7072 6f63 +6573 735f 7265 7369 6465 6e74 5f6d 656d +6f72 795f 6279 7465 7312 1e52 6573 6964 +656e 7420 6d65 6d6f 7279 2073 697a 6520 +696e 2062 7974 6573 2e18 0122 0b12 0909 +0000 0000 004b 8841 630a 1a70 726f 6365 +7373 5f73 7461 7274 5f74 696d 655f 7365 +636f 6e64 7312 3653 7461 7274 2074 696d +6520 6f66 2074 6865 2070 726f 6365 7373 +2073 696e 6365 2075 6e69 7820 6570 6f63 +6820 696e 2073 6563 6f6e 6473 2e18 0122 +0b12 0909 3d0a 172d e831 d541 4c0a 1c70 +726f 6365 7373 5f76 6972 7475 616c 5f6d +656d 6f72 795f 6279 7465 7312 1d56 6972 +7475 616c 206d 656d 6f72 7920 7369 7a65 +2069 6e20 6279 7465 732e 1801 220b 1209 +0900 0000 0020 12c0 415f 0a27 7072 6f6d +6574 6865 7573 5f64 6e73 5f73 645f 6c6f +6f6b 7570 5f66 6169 6c75 7265 735f 746f +7461 6c12 2554 6865 206e 756d 6265 7220 +6f66 2044 4e53 2d53 4420 6c6f 6f6b 7570 +2066 6169 6c75 7265 732e 1800 220b 1a09 +0900 0000 0000 0000 004f 0a1f 7072 6f6d +6574 6865 7573 5f64 6e73 5f73 645f 6c6f +6f6b 7570 735f 746f 7461 6c12 1d54 6865 +206e 756d 6265 7220 6f66 2044 4e53 2d53 +4420 6c6f 6f6b 7570 732e 1800 220b 1a09 +0900 0000 0000 0008 40cf 010a 2a70 726f +6d65 7468 6575 735f 6576 616c 7561 746f +725f 6475 7261 7469 6f6e 5f6d 696c 6c69 +7365 636f 6e64 7312 2c54 6865 2064 7572 +6174 696f 6e20 666f 7220 616c 6c20 6576 +616c 7561 7469 6f6e 7320 746f 2065 7865 +6375 7465 2e18 0222 7122 6f08 0b11 0000 +0000 0000 2240 1a12 097b 14ae 47e1 7a84 +3f11 0000 0000 0000 0000 1a12 099a 9999 +9999 99a9 3f11 0000 0000 0000 0000 1a12 +0900 0000 0000 00e0 3f11 0000 0000 0000 +0000 1a12 09cd cccc cccc ccec 3f11 0000 +0000 0000 f03f 1a12 09ae 47e1 7a14 aeef +3f11 0000 0000 0000 f03f a301 0a39 7072 +6f6d 6574 6865 7573 5f6c 6f63 616c 5f73 +746f 7261 6765 5f63 6865 636b 706f 696e +745f 6475 7261 7469 6f6e 5f6d 696c 6c69 +7365 636f 6e64 7312 5754 6865 2064 7572 +6174 696f 6e20 2869 6e20 6d69 6c6c 6973 +6563 6f6e 6473 2920 6974 2074 6f6f 6b20 +746f 2063 6865 636b 706f 696e 7420 696e +2d6d 656d 6f72 7920 6d65 7472 6963 7320 +616e 6420 6865 6164 2063 6875 6e6b 732e +1801 220b 1209 0900 0000 0000 0000 00f2 +010a 2870 726f 6d65 7468 6575 735f 6c6f +6361 6c5f 7374 6f72 6167 655f 6368 756e +6b5f 6f70 735f 746f 7461 6c12 3354 6865 +2074 6f74 616c 206e 756d 6265 7220 6f66 +2063 6875 6e6b 206f 7065 7261 7469 6f6e +7320 6279 2074 6865 6972 2074 7970 652e +1800 221b 0a0e 0a04 7479 7065 1206 6372 +6561 7465 1a09 0900 0000 0000 b880 4022 +1c0a 0f0a 0474 7970 6512 0770 6572 7369 +7374 1a09 0900 0000 0000 c05b 4022 180a +0b0a 0474 7970 6512 0370 696e 1a09 0900 +0000 0000 807b 4022 1e0a 110a 0474 7970 +6512 0974 7261 6e73 636f 6465 1a09 0900 +0000 0000 a06b 4022 1a0a 0d0a 0474 7970 +6512 0575 6e70 696e 1a09 0900 0000 0000 +807b 40c4 010a 3c70 726f 6d65 7468 6575 +735f 6c6f 6361 6c5f 7374 6f72 6167 655f +696e 6465 7869 6e67 5f62 6174 6368 5f6c +6174 656e 6379 5f6d 696c 6c69 7365 636f +6e64 7312 3751 7561 6e74 696c 6573 2066 +6f72 2062 6174 6368 2069 6e64 6578 696e +6720 6c61 7465 6e63 6965 7320 696e 206d +696c 6c69 7365 636f 6e64 732e 1802 2249 +2247 0801 1100 0000 0000 0000 001a 1209 +0000 0000 0000 e03f 1100 0000 0000 0000 +001a 1209 cdcc cccc cccc ec3f 1100 0000 +0000 0000 001a 1209 ae47 e17a 14ae ef3f +1100 0000 0000 0000 00bf 010a 2d70 726f +6d65 7468 6575 735f 6c6f 6361 6c5f 7374 +6f72 6167 655f 696e 6465 7869 6e67 5f62 +6174 6368 5f73 697a 6573 1241 5175 616e +7469 6c65 7320 666f 7220 696e 6465 7869 +6e67 2062 6174 6368 2073 697a 6573 2028 +6e75 6d62 6572 206f 6620 6d65 7472 6963 +7320 7065 7220 6261 7463 6829 2e18 0222 +4922 4708 0111 0000 0000 0000 0040 1a12 +0900 0000 0000 00e0 3f11 0000 0000 0000 +0040 1a12 09cd cccc cccc ccec 3f11 0000 +0000 0000 0040 1a12 09ae 47e1 7a14 aeef +3f11 0000 0000 0000 0040 660a 3070 726f +6d65 7468 6575 735f 6c6f 6361 6c5f 7374 +6f72 6167 655f 696e 6465 7869 6e67 5f71 +7565 7565 5f63 6170 6163 6974 7912 2354 +6865 2063 6170 6163 6974 7920 6f66 2074 +6865 2069 6e64 6578 696e 6720 7175 6575 +652e 1801 220b 1209 0900 0000 0000 00d0 +406d 0a2e 7072 6f6d 6574 6865 7573 5f6c +6f63 616c 5f73 746f 7261 6765 5f69 6e64 +6578 696e 675f 7175 6575 655f 6c65 6e67 +7468 122c 5468 6520 6e75 6d62 6572 206f +6620 6d65 7472 6963 7320 7761 6974 696e +6720 746f 2062 6520 696e 6465 7865 642e +1801 220b 1209 0900 0000 0000 0000 0067 +0a2f 7072 6f6d 6574 6865 7573 5f6c 6f63 +616c 5f73 746f 7261 6765 5f69 6e67 6573 +7465 645f 7361 6d70 6c65 735f 746f 7461 +6c12 2554 6865 2074 6f74 616c 206e 756d +6265 7220 6f66 2073 616d 706c 6573 2069 +6e67 6573 7465 642e 1800 220b 1a09 0900 +0000 0080 27cd 40c3 010a 3770 726f 6d65 +7468 6575 735f 6c6f 6361 6c5f 7374 6f72 +6167 655f 696e 7661 6c69 645f 7072 656c +6f61 645f 7265 7175 6573 7473 5f74 6f74 +616c 1279 5468 6520 746f 7461 6c20 6e75 +6d62 6572 206f 6620 7072 656c 6f61 6420 +7265 7175 6573 7473 2072 6566 6572 7269 +6e67 2074 6f20 6120 6e6f 6e2d 6578 6973 +7465 6e74 2073 6572 6965 732e 2054 6869 +7320 6973 2061 6e20 696e 6469 6361 7469 +6f6e 206f 6620 6f75 7464 6174 6564 206c +6162 656c 2069 6e64 6578 6573 2e18 0022 +0b1a 0909 0000 0000 0000 0000 6f0a 2a70 +726f 6d65 7468 6575 735f 6c6f 6361 6c5f +7374 6f72 6167 655f 6d65 6d6f 7279 5f63 +6875 6e6b 6465 7363 7312 3254 6865 2063 +7572 7265 6e74 206e 756d 6265 7220 6f66 +2063 6875 6e6b 2064 6573 6372 6970 746f +7273 2069 6e20 6d65 6d6f 7279 2e18 0122 +0b12 0909 0000 0000 0020 8f40 9c01 0a26 +7072 6f6d 6574 6865 7573 5f6c 6f63 616c +5f73 746f 7261 6765 5f6d 656d 6f72 795f +6368 756e 6b73 1263 5468 6520 6375 7272 +656e 7420 6e75 6d62 6572 206f 6620 6368 +756e 6b73 2069 6e20 6d65 6d6f 7279 2c20 +6578 636c 7564 696e 6720 636c 6f6e 6564 +2063 6875 6e6b 7320 2869 2e65 2e20 6368 +756e 6b73 2077 6974 686f 7574 2061 2064 +6573 6372 6970 746f 7229 2e18 0122 0b12 +0909 0000 0000 00e8 8d40 600a 2670 726f +6d65 7468 6575 735f 6c6f 6361 6c5f 7374 +6f72 6167 655f 6d65 6d6f 7279 5f73 6572 +6965 7312 2754 6865 2063 7572 7265 6e74 +206e 756d 6265 7220 6f66 2073 6572 6965 +7320 696e 206d 656d 6f72 792e 1801 220b +1209 0900 0000 0000 807a 40b7 010a 3570 +726f 6d65 7468 6575 735f 6c6f 6361 6c5f +7374 6f72 6167 655f 7065 7273 6973 745f +6c61 7465 6e63 795f 6d69 6372 6f73 6563 +6f6e 6473 1231 4120 7375 6d6d 6172 7920 +6f66 206c 6174 656e 6369 6573 2066 6f72 +2070 6572 7369 7374 696e 6720 6561 6368 +2063 6875 6e6b 2e18 0222 4922 4708 6f11 +1c2f dd24 e68c cc40 1a12 0900 0000 0000 +00e0 3f11 8d97 6e12 8360 3e40 1a12 09cd +cccc cccc ccec 3f11 0ad7 a370 3d62 6b40 +1a12 09ae 47e1 7a14 aeef 3f11 7b14 ae47 +e1b6 7240 6a0a 2f70 726f 6d65 7468 6575 +735f 6c6f 6361 6c5f 7374 6f72 6167 655f +7065 7273 6973 745f 7175 6575 655f 6361 +7061 6369 7479 1228 5468 6520 746f 7461 +6c20 6361 7061 6369 7479 206f 6620 7468 +6520 7065 7273 6973 7420 7175 6575 652e +1801 220b 1209 0900 0000 0000 0090 407a +0a2d 7072 6f6d 6574 6865 7573 5f6c 6f63 +616c 5f73 746f 7261 6765 5f70 6572 7369 +7374 5f71 7565 7565 5f6c 656e 6774 6812 +3a54 6865 2063 7572 7265 6e74 206e 756d +6265 7220 6f66 2063 6875 6e6b 7320 7761 +6974 696e 6720 696e 2074 6865 2070 6572 +7369 7374 2071 7565 7565 2e18 0122 0b12 +0909 0000 0000 0000 0000 ac01 0a29 7072 +6f6d 6574 6865 7573 5f6c 6f63 616c 5f73 +746f 7261 6765 5f73 6572 6965 735f 6f70 +735f 746f 7461 6c12 3454 6865 2074 6f74 +616c 206e 756d 6265 7220 6f66 2073 6572 +6965 7320 6f70 6572 6174 696f 6e73 2062 +7920 7468 6569 7220 7479 7065 2e18 0022 +1b0a 0e0a 0474 7970 6512 0663 7265 6174 +651a 0909 0000 0000 0000 0040 222a 0a1d +0a04 7479 7065 1215 6d61 696e 7465 6e61 +6e63 655f 696e 5f6d 656d 6f72 791a 0909 +0000 0000 0000 1440 d601 0a2d 7072 6f6d +6574 6865 7573 5f6e 6f74 6966 6963 6174 +696f 6e73 5f6c 6174 656e 6379 5f6d 696c +6c69 7365 636f 6e64 7312 584c 6174 656e +6379 2071 7561 6e74 696c 6573 2066 6f72 +2073 656e 6469 6e67 2061 6c65 7274 206e +6f74 6966 6963 6174 696f 6e73 2028 6e6f +7420 696e 636c 7564 696e 6720 6472 6f70 +7065 6420 6e6f 7469 6669 6361 7469 6f6e +7329 2e18 0222 4922 4708 0011 0000 0000 +0000 0000 1a12 0900 0000 0000 00e0 3f11 +0000 0000 0000 0000 1a12 09cd cccc cccc +ccec 3f11 0000 0000 0000 0000 1a12 09ae +47e1 7a14 aeef 3f11 0000 0000 0000 0000 +680a 2770 726f 6d65 7468 6575 735f 6e6f +7469 6669 6361 7469 6f6e 735f 7175 6575 +655f 6361 7061 6369 7479 122e 5468 6520 +6361 7061 6369 7479 206f 6620 7468 6520 +616c 6572 7420 6e6f 7469 6669 6361 7469 +6f6e 7320 7175 6575 652e 1801 220b 1209 +0900 0000 0000 0059 4067 0a25 7072 6f6d +6574 6865 7573 5f6e 6f74 6966 6963 6174 +696f 6e73 5f71 7565 7565 5f6c 656e 6774 +6812 2f54 6865 206e 756d 6265 7220 6f66 +2061 6c65 7274 206e 6f74 6966 6963 6174 +696f 6e73 2069 6e20 7468 6520 7175 6575 +652e 1801 220b 1209 0900 0000 0000 0000 +009e 020a 3070 726f 6d65 7468 6575 735f +7275 6c65 5f65 7661 6c75 6174 696f 6e5f +6475 7261 7469 6f6e 5f6d 696c 6c69 7365 +636f 6e64 7312 2354 6865 2064 7572 6174 +696f 6e20 666f 7220 6120 7275 6c65 2074 +6f20 6578 6563 7574 652e 1802 2260 0a15 +0a09 7275 6c65 5f74 7970 6512 0861 6c65 +7274 696e 6722 4708 3711 0000 0000 0000 +2840 1a12 0900 0000 0000 00e0 3f11 0000 +0000 0000 0000 1a12 09cd cccc cccc ccec +3f11 0000 0000 0000 0000 1a12 09ae 47e1 +7a14 aeef 3f11 0000 0000 0000 0840 2261 +0a16 0a09 7275 6c65 5f74 7970 6512 0972 +6563 6f72 6469 6e67 2247 0837 1100 0000 +0000 002e 401a 1209 0000 0000 0000 e03f +1100 0000 0000 0000 001a 1209 cdcc cccc +cccc ec3f 1100 0000 0000 0000 001a 1209 +ae47 e17a 14ae ef3f 1100 0000 0000 0008 +4069 0a29 7072 6f6d 6574 6865 7573 5f72 +756c 655f 6576 616c 7561 7469 6f6e 5f66 +6169 6c75 7265 735f 746f 7461 6c12 2d54 +6865 2074 6f74 616c 206e 756d 6265 7220 +6f66 2072 756c 6520 6576 616c 7561 7469 +6f6e 2066 6169 6c75 7265 732e 1800 220b +1a09 0900 0000 0000 0000 0060 0a21 7072 +6f6d 6574 6865 7573 5f73 616d 706c 6573 +5f71 7565 7565 5f63 6170 6163 6974 7912 +2c43 6170 6163 6974 7920 6f66 2074 6865 +2071 7565 7565 2066 6f72 2075 6e77 7269 +7474 656e 2073 616d 706c 6573 2e18 0122 +0b12 0909 0000 0000 0000 b040 da01 0a1f +7072 6f6d 6574 6865 7573 5f73 616d 706c +6573 5f71 7565 7565 5f6c 656e 6774 6812 +a701 4375 7272 656e 7420 6e75 6d62 6572 +206f 6620 6974 656d 7320 696e 2074 6865 +2071 7565 7565 2066 6f72 2075 6e77 7269 +7474 656e 2073 616d 706c 6573 2e20 4561 +6368 2069 7465 6d20 636f 6d70 7269 7365 +7320 616c 6c20 7361 6d70 6c65 7320 6578 +706f 7365 6420 6279 206f 6e65 2074 6172 +6765 7420 6173 206f 6e65 206d 6574 7269 +6320 6661 6d69 6c79 2028 692e 652e 206d +6574 7269 6373 206f 6620 7468 6520 7361 +6d65 206e 616d 6529 2e18 0122 0b12 0909 +0000 0000 0000 0000 d902 0a29 7072 6f6d +6574 6865 7573 5f74 6172 6765 745f 696e +7465 7276 616c 5f6c 656e 6774 685f 7365 +636f 6e64 7312 2141 6374 7561 6c20 696e +7465 7276 616c 7320 6265 7477 6565 6e20 +7363 7261 7065 732e 1802 2282 010a 0f0a +0869 6e74 6572 7661 6c12 0331 3573 226f +0804 1100 0000 0000 804d 401a 1209 7b14 +ae47 e17a 843f 1100 0000 0000 002c 401a +1209 9a99 9999 9999 a93f 1100 0000 0000 +002c 401a 1209 0000 0000 0000 e03f 1100 +0000 0000 002e 401a 1209 cdcc cccc cccc +ec3f 1100 0000 0000 002e 401a 1209 ae47 +e17a 14ae ef3f 1100 0000 0000 002e 4022 +8101 0a0e 0a08 696e 7465 7276 616c 1202 +3173 226f 083a 1100 0000 0000 003c 401a +1209 7b14 ae47 e17a 843f 1100 0000 0000 +0000 001a 1209 9a99 9999 9999 a93f 1100 +0000 0000 0000 001a 1209 0000 0000 0000 +e03f 1100 0000 0000 0000 001a 1209 cdcc +cccc cccc ec3f 1100 0000 0000 00f0 3f1a +1209 ae47 e17a 14ae ef3f 1100 0000 0000 +00f0 3f \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz new file mode 100644 index 00000000000..62fccb61650 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz @@ -0,0 +1,129 @@ +1f8b 0808 efa0 c754 0003 7072 6f74 6f62 +7566 00ed 594d 8c1c c515 9eb1 8d3d 5b86 +6037 265e 8c4d ca03 c4bb ceee cc9a 9f58 +01cc f6ca 4424 041b 8837 21c8 24ed daee +9a99 cef6 1f55 d578 c7e4 b004 0e39 8088 +8448 048a 124b 4442 9110 e110 25b9 c54a +9072 01c5 9724 4a24 2472 413e 448a 8592 +1b87 bcea aeda eeea 99d9 3530 49a4 68e7 +b0bb 5355 fdde abf7 bef7 bdf7 7a3f 6ca0 +664f 88c4 61f4 8994 72e1 7829 23c2 8f23 +27f4 5d16 73ea c691 c7ad cf2d f628 fed2 +e2e2 c358 9dc3 0111 3472 7dca b11f e1f2 +d9d6 e496 e6a3 e86a b4a3 4722 2fa0 ccaa +b79b f737 6abb 6bea b3cf 9ac8 ff78 6fbe +bcf6 cedb f2f3 7763 ed8d fbff 766e cf1b +ff28 d69a df44 5621 7847 9bc0 2fc1 c727 +7e09 ed2d c45f dd26 89df 0ea9 60be 3b46 +1d67 d0f5 850e 94e9 008f b2fe f834 74d0 +8d85 865d 8506 8791 a84b ffa3 de12 8475 +e938 2352 f116 208c c701 e563 84d4 e368 +77a1 617b bbcb 48d2 1b9f f4d3 6857 21fd +aa76 8f92 647c c2bf 85ae 2b84 37da 5c40 +e6ba 6374 8de9 fc84 c590 0c3d 9aca f0de +bdfb f40b bffd 5763 fe9f 7659 8314 f0fb +9fbf 6897 35b4 dfbd 65fb d397 7f60 9735 +1c43 7f7e f5cd 975e b3df 6fa0 bd06 fb70 +ff1c 7596 fa82 720b 0f50 8edc cce8 263b +b0c9 339b 3cb3 c933 5afa ff2f cfc8 13f6 +5b17 ed01 0d73 cc1e d090 af99 1a60 ed3b +e8ba 32cd 7047 c482 04d6 cd8b f217 8ed2 +7089 321c 770c bae1 3824 1e6d 4dd6 9af7 +a29d 689b 1b7b d4da 7adb dcdc 085b d135 +68bb fc33 f6ac ad00 cd7d 13b9 b5ab 27ec +4b0d 34a9 b4f3 0470 45cb 2c77 b0c4 72f9 +ee26 cd7d 02ec 6cd2 dc26 cd7d 6ce1 ff73 +9a7b ef17 1f0e d2dc 1d3f 19a4 b9c6 f941 +9a43 e7ed c7d1 0d20 d5a5 9c3b 6e92 3a6a +2053 6437 9793 5dca 81ea c006 ccfb 5cd0 +101f 7ff8 6b58 f821 d04e 4223 2169 676d +8eab 3577 028d fd34 91dd dac5 f987 90a5 +8577 6316 a7c2 8f80 bf0e 9f5c 23cf 6215 +8b1e 11d8 4d19 0391 411f d315 9f8b d664 +bdb9 d352 b458 7bc4 7e00 5dab e585 64c5 +e9c0 9439 7582 acf8 611a 9618 3906 ab70 +c70f 28f6 2877 999f 8898 7153 d405 fb38 +daa5 45c9 f399 2c7c f2a3 c838 669f 4407 +b40c 6062 df03 cb9d 9086 31e4 79ce d437 +7d55 2de3 7c39 e3e9 124d 97c4 7de5 7b0b +2eda a7c5 018e 9870 a48f 7544 accf 9f92 +6bb9 dfc1 4040 0156 a741 6ae4 529c 46fe +0aa6 49ec f68c 88e4 3a8e a1bd b397 8efc +71e1 41b4 5feb 78d2 6722 2581 69f1 81af +e7ab 1b1a 8cad 0b0b 0e3a 5420 d2f1 22b0 +db73 8238 5e4e 13a7 43fc 2005 af28 24dd +2a6b 5611 a2fb 4e9e 9a3d 751f cecf 627d +56c3 47a3 ff21 f499 51f2 b5dc 03eb c8ad +c86b d87f a8a3 c325 81f4 4912 a404 025b +7e81 1104 bef6 f88c 94ad b770 2786 1c08 +02ac 9e82 25c0 6c0c 38a5 6e2a a82c b94f +34e3 c64e 95ba 4d99 6c4f ed91 e9f6 ac91 +e2af bc2c 3f3f 9bff 88f4 7079 7e90 1e2e +cfbf 5a47 5f28 5d28 885d 8827 871b 912e +75dc 1e75 9793 d88f c488 fb3d 6adc 6f2a +7b27 536c 4f63 1fd0 068e 94b7 2c64 0118 +6615 3654 5dce 9801 58d5 8353 69b4 5cc9 +925a ed83 3a9a 5ac7 4878 0432 50c7 f376 +6993 a8b4 58d9 2199 924c f97d a92f f1ef +332c fa49 d66e dd88 3e85 b6c9 2fd6 7697 +5122 a88e faaf 57ed e67e 74ad dadc 0122 +38f0 8ade bd70 da6e 4eca 4e2d dbdd 9af8 +d15a 0ff6 94dd bc09 ca52 be33 21a0 6e73 +d9ce e9fd f3cb 7673 1ff4 6ff9 fe55 6964 +3efb 561d dd33 f2ce 7ee4 01bb 455d 6789 +08b7 e7e4 6fc5 fa66 6c8e 3e92 9248 00ff +f00c 78d9 49ac 1fac be48 2b9e 9330 fc32 +d486 fa58 aacf 6fea 68f6 4a6f 9175 a0d6 +8269 f69a c1b9 fd79 973a 5504 5623 08c2 +921f 991e b8c0 6071 cbd7 aa17 182c 6eb0 +d641 731b db0f 8d59 0a40 2409 717d d187 +061f 10a8 bf69 a65d bb48 76d8 44f8 453b +44ad 2b55 13d0 a82b 7a39 b50c fae1 2cf1 +85d4 0219 b7a4 9452 af9a 4f5d d45e 475b +17c6 10ea 399c 8449 60b2 6f35 abd4 11ac +9f29 b3e5 eaa1 77ec dfd5 d1d1 7514 010d +fa9e 9330 1ac4 c4ab 4e49 fd61 0ad5 d962 +5862 b443 1953 1726 388a a3d9 acec cb82 +092d 07e0 bb85 177b 3e98 2849 46fa c377 +73b2 9215 3a15 1ea4 8107 c9b0 4403 e5ac +8112 121b 8c6f de41 15be 8c5d 6495 e7d6 +6d59 ecf3 1e64 807f 4a8d 4096 76d9 d346 +70f0 0bf6 8fea e8b3 57a4 905b ee3a ca4a +1a66 a0c4 b841 ea49 37b9 411c 51cd b3c0 +d82d dad2 5fce fa30 47a6 02dc 58d8 396d +5877 e979 fbcc c6c6 e57e b70e 0d37 2edf +1d71 fdd5 73f6 afea e8ce 911a 14f9 9608 +aff4 df82 230b 98a7 6148 5896 7305 c149 +1a51 0f4a 0f50 023c 925d 5933 45bc 7b7f +fbdd 5bde 7fee 6d83 299e ff61 643d 73e6 +5e83 29a0 254d 8e2d 2d1b 4c91 95e8 5f32 +fbdb eb24 95b6 bb42 1453 05c6 ab74 a19e +18c6 16df b7cf ad43 aaa6 2a45 1677 ad0b +14cd 1910 930d 54d7 6aaf d7d1 f448 dd79 +6c4b b5f8 8ea1 ac91 23e0 6315 6360 e4e6 +6174 406d 5e1f 12e8 2768 44a0 7905 3e51 +005c 3bbb c7fe 9359 7ea2 58f8 1d45 007c +78d5 fcc6 83f9 2adc be5c 8638 8db2 f4c9 +de55 6043 0e54 a358 f634 3ac3 3c16 2709 +a498 7168 ad2a 8d67 a8eb 196d b379 ad0a +c65a c38a d1b0 6b0c 09f7 6376 17dd ba81 +2285 b0b6 598e 8629 50f0 1a0a ab1f 6f31 +ea2c 4b03 ea14 6df2 88ee f3e6 c1ee 1acb +272b 4db5 1c80 2732 8919 681a 996d 1029 +88c6 51e5 d1a9 613d c215 46a3 6137 09fa +7459 c304 0303 9967 aa68 7d22 15be 9175 +55f7 5426 a5d9 6159 9739 a678 66e4 c474 +061d 2c69 d24d 4005 5433 c72b 80ca f6b3 +10a4 d159 e60b c821 dd1d 98a1 7ed3 fe6b +dd98 c94c 0d0a 4daf d58f 0f90 952f 6868 +8268 843e fc45 c9f0 f238 76e3 3061 8017 +9ecd 5dba 5da1 2b09 140d 4fd2 0e14 439c +bfee c284 67df f246 0adc 0350 ebab 02a9 +9b2b 7559 9003 5887 1fd3 5518 ff65 8b11 +a75c b223 398a 81e7 d5ed d6e6 f183 0b6e +3628 eb7d 2042 2ace 5279 1597 9124 7f0b +fbdd 3acc 1e0d 7dc4 da7a e44e 0e43 e2b6 +1c19 ab27 860c 8933 f6e0 9038 3304 7dad +214d 706b 4813 dcb2 9b4f d781 900b 23b6 +1c91 36dc a5f6 eff9 af0c aaff 06f1 48e5 +4433 2000 00 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz new file mode 100644 index 00000000000..3f8199dfb26 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz @@ -0,0 +1,163 @@ +1f8b 0808 2aa1 c754 0003 7465 7874 00b5 +5b5d 939b 3816 7def 5fa1 ea79 99a9 4d3c +601b db3c f4c3 5426 55f3 309b ca6e 7ab7 +6a9e 281a d436 150c 04c4 a4bd 5df3 dff7 +4a88 361f 025d 094f 1e92 34e8 1cae 8ea4 +ab7b 04fd 03f9 ede3 ef9f c989 b122 28e9 +b79a 562c 88eb 3264 499e 05e7 242a f38a +4679 1657 e4f1 44c9 6f8f 8f9f 896c 46d2 +90d1 2c4a 6845 928c 749b aeee 7e20 8f7f +7cfe 8861 adea f339 2c2f 77fa a6af a730 +8b53 5a3e dcff 7cff ee5b 1d66 2c49 e9c3 +bdb3 f2ee ff22 ce12 027f 3101 9621 80ee +7659 90a8 28af 3366 8eeb 2042 f887 558b +7553 d158 a8a7 a4b1 d450 7259 2a69 84ee +e28a e4e7 3365 6512 dd40 d429 2e1b 6527 +b96c e5ed 10da 6a6c 4c31 0043 cbf2 7213 +9915 4c96 22ab 9816 48dc d02d 10d8 8440 +050d ca30 3bd2 db89 ace2 5b22 b592 6fa9 +e092 74a9 ec46 3403 0216 9647 7a8b cc3c +c565 29ba 9a6b 81e0 2de1 02b1 cd28 3a60 +f8b9 ca53 5a2d 2f1c 2698 2c44 9e62 b294 +f84a 6729 b029 4107 7a2c c3e2 b458 5a05 +8b85 ac2a 164b 491b 2a4b 394d c01d d889 +86c5 6225 c724 1642 2a48 2c75 144c 9632 +1a60 3ba8 8ac1 ed68 f96a 57f2 5868 a9e6 +b194 b325 b354 d40c 7e05 1665 0e45 dc89 +d68a bdca dd38 fbd5 7aef dd84 90cb e21e +bcc3 6ab7 59df 8690 336e 9cc3 7eb5 396c +8df5 eeb0 425c 7bff 70d8 ad3c 47fe 712d +46a0 4fe8 fa60 96c7 16bc 4afe 4783 a70b +a30a dfcd ef09 cf2d eeab cd76 07af 74d8 +d7fb 26b6 1a81 524c 6a0c 6a16 a675 cd9d +a67a abac 0c07 e98f d158 ac0c 5827 3c29 +c694 819d 9144 0fb1 34ba 6604 6889 4c2c +edb4 4e73 2674 4e2c 1cce cab1 9ac0 4dd4 +427a d359 ad26 fca4 4629 2d6a 81f5 3427 +31d6 0c6b 32f5 ca4d 5942 8c7e 7aac a587 +3423 3051 0fed 1667 959b f477 1ad5 1038 +2b33 6802 c7aa 6560 fb26 b59a b16a 334a +a150 c6ae 0e0b c5ea 83f4 6f93 da4c f8ae +195d b408 537b 8644 6215 c119 b149 41d4 +0e6a 460f 1dc0 c267 e1c1 5851 d08e 6a52 +9749 1f34 230d 0283 334c 6bdf b527 f017 +1368 1866 0cd0 66bb 3d1c b07a 619c 4e15 +b09c 8529 7914 7f67 f5f9 8996 247f ee39 +9e8a 9cc3 982a 8d4e 0b17 4fa6 e59d e2de +6b94 c7d0 edb5 e3dc bf53 4ac3 ff93 c70f +f7b0 8728 e3ac 0ac8 9c74 c292 3537 359e +6ccc 3030 65a3 0638 5786 87f9 96b0 79dc +8c31 1bb7 9d73 6673 1169 ad99 2918 ad85 +de9c e914 195b 2dbd 2e08 8cb1 3fb3 62c0 +eb84 7368 5ab1 d456 0ba1 1812 6868 d22c +f046 9269 6d1a 46b0 91e3 c2c9 a587 5939 +356b 1673 e1f4 5e0d 2ddf d870 1988 8800 +1bdb 352b 0623 0911 860d 239f c279 e1a4 +c300 0d3d 9b05 1e2d 19ca b5e9 0453 1a30 +bd5c 3898 8171 33c4 a245 d25a 379d 4023 +27a6 1747 0fc1 bb37 3328 5a16 9d7f d3a9 +32f4 637a 51b4 0823 0b67 8c46 2b83 3071 +3a71 148e 4caf 0f06 84f4 71ce d65f 4021 +7c98 e31d 9650 341c bb2d 52b1 9e27 5b6f +f79d 7758 5ae1 a6fc 1c5c 8f68 05cd 8b3a +685f 7a75 5d5d 5d81 a703 1252 5d2a 46cf +e4c3 e7ff 1096 9cc1 3515 3463 dc35 0d3f +1c9d 666c 8dde 740b 1819 6f18 d931 2ff3 +9a25 1938 af4f 6f16 b373 919d 4246 a2ba +2c21 9ef4 42e8 4b52 b151 309d f6c7 b03e +d23b c58d bd33 7cf4 397c 099e e38a fc33 +7c49 cef5 b963 7173 e83d 7986 7124 31ad +a232 2958 5e8e 2568 f1fd 47b6 570f aebf +1e3e 91f3 8a9b 9f0c 1ff5 06ec 3feb edf2 +7a34 e230 6992 1834 0bce f49c 432d d498 +db7f cbab a4b9 2acc f1d8 1bcf 73f4 4350 +b7f1 569b c3de f1fc 35fd 87b3 1f86 068b +bc64 019f 66ed fc20 5ff8 a566 e681 2630 +91db c610 6116 5152 67c9 0ba1 451e 9de6 +e6a4 82b8 1fac a281 bbda aed7 9bdd c1df +1e36 3b88 7624 e49f 49c9 ea30 edf7 efbf +cd45 9c8c 4a86 7e60 ca26 de6a eb6e f707 +dfe5 2a1e 3a71 c9a5 1ec4 1974 290e d23c +ff5a 17c1 7398 a435 0c47 bbc0 41c4 eb8c +fef5 d397 f75f 7e25 4d53 d236 ed86 8a22 +edac 7154 7b47 1735 225a 7d94 d8e8 da76 +7b45 54f4 cf30 ad43 587c dd4f 05d2 34e9 +7e63 dfde 21cf 3964 cd34 2512 0497 2051 +e590 9c68 5433 aa8a 5747 df9e 3ae1 21af +ddbd c671 c596 698b f696 a017 81c5 2725 +d660 5334 df70 89bb 3641 8839 45d6 1bc5 +9449 f308 966c 05d8 f048 83e8 44a3 af45 +9e64 0c33 837e 14bf 9871 bdfb 1349 20ff +c12c e5f3 e84a 0549 e5bd cc31 f218 45ec +d650 46c6 d0aa cebe 2a17 8761 606f a9c8 +12af 5ae4 430a 0815 76ab ee6a 6783 6365 +d186 6f87 a55c 504f 17be 1124 2561 9742 +b9a6 e69f a148 06b3 8057 fe98 87fb a8a4 +21e3 8706 9e7f 30c5 42ec 1594 27e2 6ba4 +ad31 38c9 00e8 af1d 5320 2bc3 ace2 27e9 +00df ba9e 29bc ceae 4fd6 8d63 92c5 5080 +65c7 e029 64d1 2968 7ecd e8d2 9f0d ff92 +0bb4 1259 5234 242d 6ef8 8b49 5798 7e7c +31cf 5664 5163 92f9 dcb6 8cce bf31 dd72 +3e91 1117 5234 29d2 359d 3dcd 8b99 fe74 +799b 28cd bc69 9afc 784d 126d 1284 95d6 +34f9 c978 e234 9ca6 3345 a046 5363 bd00 +ef2f c55b 1088 d136 c518 0fef b79a d690 +6dc2 228c 1276 11c9 feed 0759 ddbf 8db3 +686b 3086 036e cdd6 3505 7377 fc7b 53c3 +0ea5 343b b2d3 a052 6d27 e4f7 3061 bc3f +b07b 3fc9 eed1 d8b8 5ff2 1166 bd92 204c +f63e 5270 f971 5085 e722 a573 9bb1 6c41 +5a08 a627 4a72 ed2e 3c81 db38 dbbd bee6 +4a32 a8de 9238 284a 9ae6 613c 7a73 ade8 +996c 7a7d 815d d267 5a96 72ec 4292 e5d9 +7b71 c8c0 5d72 454b d8ab 5640 9480 16bc +f6e2 439b 444d 0dc7 dd7b cd62 4889 316c +6c4f 3495 e38e dacc 6603 47a8 368b d7cf +0569 3445 49c0 0f1e 9af2 549e b38c aab2 +ced1 84d8 b805 58df cbf1 4334 337b 0c70 +1dcf 37ea cc6c 473a d1bf 03b7 16a5 75cc +073e 4af3 8cb6 0535 94e6 2bba 6a7f f89e +b013 0c32 4c8c ab06 883d a71f 9141 af79 +8f11 8598 8434 f373 a2c7 f2a6 f978 4920 +2e6a d978 bbd6 e753 591e 778a 88ce 6f9b +ffd2 6ec9 3cf4 6b99 c88b 0289 e323 4543 +a80a 8450 fade cc3e 4ebb ffcf a147 75c0 +c659 6df6 fb1b 9035 47c6 9b95 b7f1 6fc1 +26e8 76eb dd6a bbdb d8f1 3515 8303 c3bb +9af5 16b3 1cb2 82d8 e3a7 88a2 8490 9971 +5048 4800 b68e 98e0 d74c f509 14ac 54d3 +1e75 6a88 c914 d596 12b0 7017 f710 5750 +2831 fa24 d42c 7d8d ad97 f9c1 ded7 8f9e +a2dd 1c87 88a1 b39f 2980 27a0 e730 8147 +6661 16f1 ad57 a63e f1a6 4521 5296 b3e4 +59d6 0895 daa7 fede 5c24 df7a e6a7 a299 +d88e c467 46a4 4703 1e28 e787 41ed 8e15 +9779 51c0 96d5 6ba4 dc97 10d1 2872 a11e +356f 930d f123 1f6b 8ab7 2018 3b5f 04a6 +c964 aaa5 d107 232c 906a 9427 d7f8 2cfb +6875 cfb6 761d 6cf8 4ac3 a30a 5b66 2aa3 +e8a7 32d3 4c5b 55dc 659d d2e0 7a0c 8f3e +bc27 1ca8 39b3 c771 2b56 0f0a f82a 5a35 +f945 880a eb5a f5ae fff6 bca3 c572 2bde +d189 048a 58bc 0557 91ff 3538 aac7 b135 +6fc6 27f8 fa25 8c71 bf4b b854 c67f c340 +4d10 2f1f a929 62f1 8bb7 8b87 eaca 0eda +9a4b 3b1e ab1e a1eb 2116 bce2 ade7 b004 +114b fd0a 997d fba9 a157 d41e 1a84 2a69 +b547 1d83 ccfc 61b0 4388 db22 5dd5 d9f7 +3261 b01f b507 33aa d027 5847 1976 a2dd +d6f1 77da 5865 26fe 30aa 5d13 46cf fd8d +6022 70f2 915b 38de 1cc4 3c17 25cc 854a +bc4b 6d8f 9ce8 4b01 c621 e665 22b8 72d2 +7c8e 48c2 4afc d41c b7c1 08c2 34ba 48a7 +de1e c149 d580 07f6 2bf8 4b59 0e29 bba3 +9168 66fb 69a2 0b78 7558 c214 904d df3e +2ef8 2512 5f09 b4b7 a1f6 a5ec 3be5 6a44 +6558 a887 5143 a9d8 6ee6 11af edf5 877b +d71b 7ca2 245e 1bbb db1b 9179 3724 f346 +19c5 9ecb bf25 9729 9948 997d 42fe 7ad0 +84a1 c992 238e b55d 8f54 53c0 b90d d568 +1fb4 a6ba 1dd3 e813 017b 2643 aae1 c8f3 +41f3 168d 7bf3 71df feee ff2d f9e8 431a +5200 00 \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/text b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text similarity index 99% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/text rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text index 1b2b3022131..f3d8c378443 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/testdata/text +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text @@ -175,9 +175,9 @@ http_response_size_bytes_count{handler="prometheus"} 119 # HELP process_cpu_seconds_total Total user and system CPU time spent in seconds. # TYPE process_cpu_seconds_total counter process_cpu_seconds_total 0.55 -# HELP process_goroutines Number of goroutines that currently exist. -# TYPE process_goroutines gauge -process_goroutines 70 +# HELP go_goroutines Number of goroutines that currently exist. +# TYPE go_goroutines gauge +go_goroutines 70 # HELP process_max_fds Maximum number of open file descriptors. # TYPE process_max_fds gauge process_max_fds 8192 diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/create.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create.go similarity index 88% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/text/create.go rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create.go index 44304599466..0bb9c14cc2b 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/create.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create.go @@ -11,14 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package text contains helper functions to parse and create text-based -// exchange formats. The package currently supports (only) version 0.0.4 of the -// exchange format. Should other versions be supported in the future, some -// versioning scheme has to be applied. Possibilities include separate packages -// or separate functions. The best way depends on the nature of future changes, -// which is the reason why no versioning scheme has been applied prematurely -// here. -package text +package expfmt import ( "bytes" @@ -27,8 +20,8 @@ import ( "math" "strings" - "github.com/prometheus/client_golang/model" dto "github.com/prometheus/client_model/go" + "github.com/prometheus/common/model" ) // MetricFamilyToText converts a MetricFamily proto message into text format and @@ -48,9 +41,6 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { if name == "" { return written, fmt.Errorf("MetricFamily has no name: %s", in) } - if in.Type == nil { - return written, fmt.Errorf("MetricFamily has no type: %s", in) - } // Comments, first HELP, then TYPE. if in.Help != nil { @@ -79,7 +69,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { case dto.MetricType_COUNTER: if metric.Counter == nil { return written, fmt.Errorf( - "expected counter in metric %s", metric, + "expected counter in metric %s %s", name, metric, ) } n, err = writeSample( @@ -90,7 +80,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { case dto.MetricType_GAUGE: if metric.Gauge == nil { return written, fmt.Errorf( - "expected gauge in metric %s", metric, + "expected gauge in metric %s %s", name, metric, ) } n, err = writeSample( @@ -101,7 +91,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { case dto.MetricType_UNTYPED: if metric.Untyped == nil { return written, fmt.Errorf( - "expected untyped in metric %s", metric, + "expected untyped in metric %s %s", name, metric, ) } n, err = writeSample( @@ -112,7 +102,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { case dto.MetricType_SUMMARY: if metric.Summary == nil { return written, fmt.Errorf( - "expected summary in metric %s", metric, + "expected summary in metric %s %s", name, metric, ) } for _, q := range metric.Summary.Quantile { @@ -144,7 +134,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { case dto.MetricType_HISTOGRAM: if metric.Histogram == nil { return written, fmt.Errorf( - "expected summary in metric %s", metric, + "expected histogram in metric %s %s", name, metric, ) } infSeen := false @@ -191,7 +181,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { ) default: return written, fmt.Errorf( - "unexpected type in metric %s", metric, + "unexpected type in metric %s %s", name, metric, ) } written += n diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/create_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go similarity index 98% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/text/create_test.go rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go index fe938de80c7..e4cc5d803bb 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/create_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package text +package expfmt import ( "bytes" @@ -20,6 +20,7 @@ import ( "testing" "github.com/golang/protobuf/proto" + dto "github.com/prometheus/client_model/go" ) @@ -311,6 +312,24 @@ request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 +`, + }, + // 6: No metric type, should result in default type Counter. + { + in: &dto.MetricFamily{ + Name: proto.String("name"), + Help: proto.String("doc string"), + Metric: []*dto.Metric{ + &dto.Metric{ + Counter: &dto.Counter{ + Value: proto.Float64(math.Inf(-1)), + }, + }, + }, + }, + out: `# HELP name doc string +# TYPE name counter +name -Inf `, }, } @@ -378,22 +397,7 @@ func testCreateError(t testing.TB) { }, err: "MetricFamily has no name", }, - // 2: No metric type. - { - in: &dto.MetricFamily{ - Name: proto.String("name"), - Help: proto.String("doc string"), - Metric: []*dto.Metric{ - &dto.Metric{ - Untyped: &dto.Untyped{ - Value: proto.Float64(math.Inf(-1)), - }, - }, - }, - }, - err: "MetricFamily has no type", - }, - // 3: Wrong type. + // 2: Wrong type. { in: &dto.MetricFamily{ Name: proto.String("name"), diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/parse.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse.go similarity index 92% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/text/parse.go rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse.go index e317d6850be..84433bc4f6e 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/parse.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package text +package expfmt import ( "bufio" @@ -25,7 +25,7 @@ import ( dto "github.com/prometheus/client_model/go" "github.com/golang/protobuf/proto" - "github.com/prometheus/client_golang/model" + "github.com/prometheus/common/model" ) // A stateFn is a function that represents a state in a state machine. By @@ -46,9 +46,9 @@ func (e ParseError) Error() string { return fmt.Sprintf("text format parsing error in line %d: %s", e.Line, e.Msg) } -// Parser is used to parse the simple and flat text-based exchange format. Its +// TextParser is used to parse the simple and flat text-based exchange format. Its // nil value is ready to use. -type Parser struct { +type TextParser struct { metricFamiliesByName map[string]*dto.MetricFamily buf *bufio.Reader // Where the parsed input is read through. err error // Most recent error. @@ -83,14 +83,21 @@ type Parser struct { // and exactly the same label set), the resulting MetricFamily will contain // duplicate Metric proto messages. Similar is true for duplicate label // names. Checks for duplicates have to be performed separately, if required. +// Also note that neither the metrics within each MetricFamily are sorted nor +// the label pairs within each Metric. Sorting is not required for the most +// frequent use of this method, which is sample ingestion in the Prometheus +// server. However, for presentation purposes, you might want to sort the +// metrics, and in some cases, you must sort the labels, e.g. for consumption by +// the metric family injection hook of the Prometheus registry. // -// Summaries are a rather special beast. You would probably not use them in the -// simple text format anyway. This method can deal with summaries if they are -// presented in exactly the way the text.Create function creates them. +// Summaries and histograms are rather special beasts. You would probably not +// use them in the simple text format anyway. This method can deal with +// summaries and histograms if they are presented in exactly the way the +// text.Create function creates them. // // This method must not be called concurrently. If you want to parse different // input concurrently, instantiate a separate Parser for each goroutine. -func (p *Parser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) { +func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) { p.reset(in) for nextState := p.startOfLine; nextState != nil; nextState = nextState() { // Magic happens here... @@ -104,7 +111,7 @@ func (p *Parser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamil return p.metricFamiliesByName, p.err } -func (p *Parser) reset(in io.Reader) { +func (p *TextParser) reset(in io.Reader) { p.metricFamiliesByName = map[string]*dto.MetricFamily{} if p.buf == nil { p.buf = bufio.NewReader(in) @@ -125,7 +132,7 @@ func (p *Parser) reset(in io.Reader) { // startOfLine represents the state where the next byte read from p.buf is the // start of a line (or whitespace leading up to it). -func (p *Parser) startOfLine() stateFn { +func (p *TextParser) startOfLine() stateFn { p.lineCount++ if p.skipBlankTab(); p.err != nil { // End of input reached. This is the only case where @@ -144,7 +151,7 @@ func (p *Parser) startOfLine() stateFn { // startComment represents the state where the next byte read from p.buf is the // start of a comment (or whitespace leading up to it). -func (p *Parser) startComment() stateFn { +func (p *TextParser) startComment() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } @@ -205,7 +212,7 @@ func (p *Parser) startComment() stateFn { // readingMetricName represents the state where the last byte read (now in // p.currentByte) is the first byte of a metric name. -func (p *Parser) readingMetricName() stateFn { +func (p *TextParser) readingMetricName() stateFn { if p.readTokenAsMetricName(); p.err != nil { return nil } @@ -232,7 +239,7 @@ func (p *Parser) readingMetricName() stateFn { // readingLabels represents the state where the last byte read (now in // p.currentByte) is either the first byte of the label set (i.e. a '{'), or the // first byte of the value (otherwise). -func (p *Parser) readingLabels() stateFn { +func (p *TextParser) readingLabels() stateFn { // Summaries/histograms are special. We have to reset the // currentLabels map, currentQuantile and currentBucket before starting to // read labels. @@ -250,7 +257,7 @@ func (p *Parser) readingLabels() stateFn { // startLabelName represents the state where the next byte read from p.buf is // the start of a label name (or whitespace leading up to it). -func (p *Parser) startLabelName() stateFn { +func (p *TextParser) startLabelName() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } @@ -290,7 +297,7 @@ func (p *Parser) startLabelName() stateFn { // startLabelValue represents the state where the next byte read from p.buf is // the start of a (quoted) label value (or whitespace leading up to it). -func (p *Parser) startLabelValue() stateFn { +func (p *TextParser) startLabelValue() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } @@ -348,7 +355,7 @@ func (p *Parser) startLabelValue() stateFn { // readingValue represents the state where the last byte read (now in // p.currentByte) is the first byte of the sample value (i.e. a float). -func (p *Parser) readingValue() stateFn { +func (p *TextParser) readingValue() stateFn { // When we are here, we have read all the labels, so for the // special case of a summary/histogram, we can finally find out // if the metric already exists. @@ -436,7 +443,7 @@ func (p *Parser) readingValue() stateFn { // startTimestamp represents the state where the next byte read from p.buf is // the start of the timestamp (or whitespace leading up to it). -func (p *Parser) startTimestamp() stateFn { +func (p *TextParser) startTimestamp() stateFn { if p.skipBlankTab(); p.err != nil { return nil // Unexpected end of input. } @@ -462,7 +469,7 @@ func (p *Parser) startTimestamp() stateFn { // readingHelp represents the state where the last byte read (now in // p.currentByte) is the first byte of the docstring after 'HELP'. -func (p *Parser) readingHelp() stateFn { +func (p *TextParser) readingHelp() stateFn { if p.currentMF.Help != nil { p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName())) return nil @@ -477,7 +484,7 @@ func (p *Parser) readingHelp() stateFn { // readingType represents the state where the last byte read (now in // p.currentByte) is the first byte of the type hint after 'HELP'. -func (p *Parser) readingType() stateFn { +func (p *TextParser) readingType() stateFn { if p.currentMF.Type != nil { p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName())) return nil @@ -497,7 +504,7 @@ func (p *Parser) readingType() stateFn { // parseError sets p.err to a ParseError at the current line with the given // message. -func (p *Parser) parseError(msg string) { +func (p *TextParser) parseError(msg string) { p.err = ParseError{ Line: p.lineCount, Msg: msg, @@ -506,7 +513,7 @@ func (p *Parser) parseError(msg string) { // skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte // that is neither ' ' nor '\t'. That byte is left in p.currentByte. -func (p *Parser) skipBlankTab() { +func (p *TextParser) skipBlankTab() { for { if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) { return @@ -516,7 +523,7 @@ func (p *Parser) skipBlankTab() { // skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do // anything if p.currentByte is neither ' ' nor '\t'. -func (p *Parser) skipBlankTabIfCurrentBlankTab() { +func (p *TextParser) skipBlankTabIfCurrentBlankTab() { if isBlankOrTab(p.currentByte) { p.skipBlankTab() } @@ -526,7 +533,7 @@ func (p *Parser) skipBlankTabIfCurrentBlankTab() { // first byte considered is the byte already read (now in p.currentByte). The // first whitespace byte encountered is still copied into p.currentByte, but not // into p.currentToken. -func (p *Parser) readTokenUntilWhitespace() { +func (p *TextParser) readTokenUntilWhitespace() { p.currentToken.Reset() for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' { p.currentToken.WriteByte(p.currentByte) @@ -540,7 +547,7 @@ func (p *Parser) readTokenUntilWhitespace() { // p.currentToken. If recognizeEscapeSequence is true, two escape sequences are // recognized: '\\' tranlates into '\', and '\n' into a line-feed character. All // other escape sequences are invalid and cause an error. -func (p *Parser) readTokenUntilNewline(recognizeEscapeSequence bool) { +func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) { p.currentToken.Reset() escaped := false for p.err == nil { @@ -573,7 +580,7 @@ func (p *Parser) readTokenUntilNewline(recognizeEscapeSequence bool) { // The first byte considered is the byte already read (now in p.currentByte). // The first byte not part of a metric name is still copied into p.currentByte, // but not into p.currentToken. -func (p *Parser) readTokenAsMetricName() { +func (p *TextParser) readTokenAsMetricName() { p.currentToken.Reset() if !isValidMetricNameStart(p.currentByte) { return @@ -591,7 +598,7 @@ func (p *Parser) readTokenAsMetricName() { // The first byte considered is the byte already read (now in p.currentByte). // The first byte not part of a label name is still copied into p.currentByte, // but not into p.currentToken. -func (p *Parser) readTokenAsLabelName() { +func (p *TextParser) readTokenAsLabelName() { p.currentToken.Reset() if !isValidLabelNameStart(p.currentByte) { return @@ -610,7 +617,7 @@ func (p *Parser) readTokenAsLabelName() { // last read byte in p.currentByte, this method ignores p.currentByte and starts // with reading a new byte from p.buf. The first byte not part of a label value // is still copied into p.currentByte, but not into p.currentToken. -func (p *Parser) readTokenAsLabelValue() { +func (p *TextParser) readTokenAsLabelValue() { p.currentToken.Reset() escaped := false for { @@ -644,7 +651,7 @@ func (p *Parser) readTokenAsLabelValue() { } } -func (p *Parser) setOrCreateCurrentMF() { +func (p *TextParser) setOrCreateCurrentMF() { p.currentIsSummaryCount = false p.currentIsSummarySum = false p.currentIsHistogramCount = false diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/parse_test.go b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go similarity index 97% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/text/parse_test.go rename to Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go index cc3e6470f0a..589c87a9d5f 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/text/parse_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package text +package expfmt import ( "math" @@ -22,9 +22,7 @@ import ( dto "github.com/prometheus/client_model/go" ) -var parser Parser - -func testParse(t testing.TB) { +func testTextParse(t testing.TB) { var scenarios = []struct { in string out []*dto.MetricFamily @@ -419,17 +417,17 @@ request_duration_microseconds_count 2693 } } -func TestParse(t *testing.T) { - testParse(t) +func TestTextParse(t *testing.T) { + testTextParse(t) } -func BenchmarkParse(b *testing.B) { +func BenchmarkTextParse(b *testing.B) { for i := 0; i < b.N; i++ { - testParse(b) + testTextParse(b) } } -func testParseError(t testing.TB) { +func testTextParseError(t testing.TB) { var scenarios = []struct { in string err string @@ -577,12 +575,12 @@ metric_bucket{le="bla"} 3.14 } -func TestParseError(t *testing.T) { - testParseError(t) +func TestTextParseError(t *testing.T) { + testTextParseError(t) } func BenchmarkParseError(b *testing.B) { for i := 0; i < b.N; i++ { - testParseError(b) + testTextParseError(b) } } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/fingerprinting.go b/Godeps/_workspace/src/github.com/prometheus/common/model/fingerprinting.go similarity index 85% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/fingerprinting.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/fingerprinting.go index 5b2ffe3bb35..fc4de4106e8 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/fingerprinting.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/fingerprinting.go @@ -22,28 +22,23 @@ import ( // For our purposes, FNV-1A 64-bit is used. type Fingerprint uint64 -func (f Fingerprint) String() string { - return fmt.Sprintf("%016x", uint64(f)) +// FingerprintFromString transforms a string representation into a Fingerprint. +func FingerprintFromString(s string) (Fingerprint, error) { + num, err := strconv.ParseUint(s, 16, 64) + return Fingerprint(num), err } -// Less implements sort.Interface. -func (f Fingerprint) Less(o Fingerprint) bool { - return f < o -} - -// Equal implements sort.Interface. -func (f Fingerprint) Equal(o Fingerprint) bool { - return f == o -} - -// LoadFromString transforms a string representation into a Fingerprint. -func (f *Fingerprint) LoadFromString(s string) error { +// ParseFingerprint parses the input string into a fingerprint. +func ParseFingerprint(s string) (Fingerprint, error) { num, err := strconv.ParseUint(s, 16, 64) if err != nil { - return err + return 0, err } - *f = Fingerprint(num) - return nil + return Fingerprint(num), nil +} + +func (f Fingerprint) String() string { + return fmt.Sprintf("%016x", uint64(f)) } // Fingerprints represents a collection of Fingerprint subject to a given diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go b/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go new file mode 100644 index 00000000000..6459c8f791b --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go @@ -0,0 +1,188 @@ +// Copyright 2013 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 model + +import ( + "encoding/json" + "fmt" + "regexp" + "sort" + "strings" +) + +const ( + // AlertNameLabel is the name of the label containing the an alert's name. + AlertNameLabel = "alertname" + + // ExportedLabelPrefix is the prefix to prepend to the label names present in + // exported metrics if a label of the same name is added by the server. + ExportedLabelPrefix = "exported_" + + // MetricNameLabel is the label name indicating the metric name of a + // timeseries. + MetricNameLabel = "__name__" + + // SchemeLabel is the name of the label that holds the scheme on which to + // scrape a target. + SchemeLabel = "__scheme__" + + // AddressLabel is the name of the label that holds the address of + // a scrape target. + AddressLabel = "__address__" + + // MetricsPathLabel is the name of the label that holds the path on which to + // scrape a target. + MetricsPathLabel = "__metrics_path__" + + // ReservedLabelPrefix is a prefix which is not legal in user-supplied + // label names. + ReservedLabelPrefix = "__" + + // MetaLabelPrefix is a prefix for labels that provide meta information. + // Labels with this prefix are used for intermediate label processing and + // will not be attached to time series. + MetaLabelPrefix = "__meta_" + + // TmpLabelPrefix is a prefix for temporary labels as part of relabelling. + // Labels with this prefix are used for intermediate label processing and + // will not be attached to time series. This is reserved for use in + // Prometheus configuration files by users. + TmpLabelPrefix = "__tmp_" + + // ParamLabelPrefix is a prefix for labels that provide URL parameters + // used to scrape a target. + ParamLabelPrefix = "__param_" + + // JobLabel is the label name indicating the job from which a timeseries + // was scraped. + JobLabel = "job" + + // InstanceLabel is the label name used for the instance label. + InstanceLabel = "instance" + + // BucketLabel is used for the label that defines the upper bound of a + // bucket of a histogram ("le" -> "less or equal"). + BucketLabel = "le" + + // QuantileLabel is used for the label that defines the quantile in a + // summary. + QuantileLabel = "quantile" +) + +// LabelNameRE is a regular expression matching valid label names. +var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") + +// A LabelName is a key for a LabelSet or Metric. It has a value associated +// therewith. +type LabelName string + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + if err := unmarshal(&s); err != nil { + return err + } + if !LabelNameRE.MatchString(s) { + return fmt.Errorf("%q is not a valid label name", s) + } + *ln = LabelName(s) + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (ln *LabelName) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + if !LabelNameRE.MatchString(s) { + return fmt.Errorf("%q is not a valid label name", s) + } + *ln = LabelName(s) + return nil +} + +// LabelNames is a sortable LabelName slice. In implements sort.Interface. +type LabelNames []LabelName + +func (l LabelNames) Len() int { + return len(l) +} + +func (l LabelNames) Less(i, j int) bool { + return l[i] < l[j] +} + +func (l LabelNames) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func (l LabelNames) String() string { + labelStrings := make([]string, 0, len(l)) + for _, label := range l { + labelStrings = append(labelStrings, string(label)) + } + return strings.Join(labelStrings, ", ") +} + +// A LabelValue is an associated value for a LabelName. +type LabelValue string + +// LabelValues is a sortable LabelValue slice. It implements sort.Interface. +type LabelValues []LabelValue + +func (l LabelValues) Len() int { + return len(l) +} + +func (l LabelValues) Less(i, j int) bool { + return sort.StringsAreSorted([]string{string(l[i]), string(l[j])}) +} + +func (l LabelValues) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +// LabelPair pairs a name with a value. +type LabelPair struct { + Name LabelName + Value LabelValue +} + +// LabelPairs is a sortable slice of LabelPair pointers. It implements +// sort.Interface. +type LabelPairs []*LabelPair + +func (l LabelPairs) Len() int { + return len(l) +} + +func (l LabelPairs) Less(i, j int) bool { + switch { + case l[i].Name > l[j].Name: + return false + case l[i].Name < l[j].Name: + return true + case l[i].Value > l[j].Value: + return false + case l[i].Value < l[j].Value: + return true + default: + return false + } +} + +func (l LabelPairs) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelvalue_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go similarity index 66% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelvalue_test.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go index 15904edf4f1..ab17025c76a 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/labelvalue_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go @@ -18,6 +18,42 @@ import ( "testing" ) +func testLabelNames(t testing.TB) { + var scenarios = []struct { + in LabelNames + out LabelNames + }{ + { + in: LabelNames{"ZZZ", "zzz"}, + out: LabelNames{"ZZZ", "zzz"}, + }, + { + in: LabelNames{"aaa", "AAA"}, + out: LabelNames{"AAA", "aaa"}, + }, + } + + for i, scenario := range scenarios { + sort.Sort(scenario.in) + + for j, expected := range scenario.out { + if expected != scenario.in[j] { + t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j]) + } + } + } +} + +func TestLabelNames(t *testing.T) { + testLabelNames(t) +} + +func BenchmarkLabelNames(b *testing.B) { + for i := 0; i < b.N; i++ { + testLabelNames(b) + } +} + func testLabelValues(t testing.TB) { var scenarios = []struct { in LabelValues diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/labelset.go b/Godeps/_workspace/src/github.com/prometheus/common/model/labelset.go new file mode 100644 index 00000000000..142b9d1e2d3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/labelset.go @@ -0,0 +1,153 @@ +// Copyright 2013 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 model + +import ( + "encoding/json" + "fmt" + "sort" + "strings" +) + +// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet +// may be fully-qualified down to the point where it may resolve to a single +// Metric in the data store or not. All operations that occur within the realm +// of a LabelSet can emit a vector of Metric entities to which the LabelSet may +// match. +type LabelSet map[LabelName]LabelValue + +func (ls LabelSet) Equal(o LabelSet) bool { + if len(ls) != len(o) { + return false + } + for ln, lv := range ls { + olv, ok := o[ln] + if !ok { + return false + } + if olv != lv { + return false + } + } + return true +} + +// Before compares the metrics, using the following criteria: +// +// If m has fewer labels than o, it is before o. If it has more, it is not. +// +// If the number of labels is the same, the superset of all label names is +// sorted alphanumerically. The first differing label pair found in that order +// determines the outcome: If the label does not exist at all in m, then m is +// before o, and vice versa. Otherwise the label value is compared +// alphanumerically. +// +// If m and o are equal, the method returns false. +func (ls LabelSet) Before(o LabelSet) bool { + if len(ls) < len(o) { + return true + } + if len(ls) > len(o) { + return false + } + + lns := make(LabelNames, 0, len(ls)+len(o)) + for ln := range ls { + lns = append(lns, ln) + } + for ln := range o { + lns = append(lns, ln) + } + // It's probably not worth it to de-dup lns. + sort.Sort(lns) + for _, ln := range lns { + mlv, ok := ls[ln] + if !ok { + return true + } + olv, ok := o[ln] + if !ok { + return false + } + if mlv < olv { + return true + } + if mlv > olv { + return false + } + } + return false +} + +func (ls LabelSet) Clone() LabelSet { + lsn := make(LabelSet, len(ls)) + for ln, lv := range ls { + lsn[ln] = lv + } + return lsn +} + +// Merge is a helper function to non-destructively merge two label sets. +func (l LabelSet) Merge(other LabelSet) LabelSet { + result := make(LabelSet, len(l)) + + for k, v := range l { + result[k] = v + } + + for k, v := range other { + result[k] = v + } + + return result +} + +func (l LabelSet) String() string { + lstrs := make([]string, 0, len(l)) + for l, v := range l { + lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v)) + } + + sort.Strings(lstrs) + return fmt.Sprintf("{%s}", strings.Join(lstrs, ", ")) +} + +// Fingerprint returns the LabelSet's fingerprint. +func (ls LabelSet) Fingerprint() Fingerprint { + return labelSetToFingerprint(ls) +} + +// FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing +// algorithm, which is, however, more susceptible to hash collisions. +func (ls LabelSet) FastFingerprint() Fingerprint { + return labelSetToFastFingerprint(ls) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (l *LabelSet) UnmarshalJSON(b []byte) error { + var m map[LabelName]LabelValue + if err := json.Unmarshal(b, &m); err != nil { + return err + } + // encoding/json only unmarshals maps of the form map[string]T. It treats + // LabelName as a string and does not call its UnmarshalJSON method. + // Thus, we have to replicate the behavior here. + for ln := range m { + if !LabelNameRE.MatchString(string(ln)) { + return fmt.Errorf("%q is not a valid label name", ln) + } + } + *l = LabelSet(m) + return nil +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go b/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go similarity index 50% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/metric.go index 32f9d7fbca7..25fc3c94258 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/metric.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go @@ -14,7 +14,6 @@ package model import ( - "encoding/json" "fmt" "sort" "strings" @@ -24,19 +23,27 @@ var separator = []byte{0} // A Metric is similar to a LabelSet, but the key difference is that a Metric is // a singleton and refers to one and only one stream of samples. -type Metric map[LabelName]LabelValue +type Metric LabelSet -// Equal compares the fingerprints of both metrics. +// Equal compares the metrics. func (m Metric) Equal(o Metric) bool { - return m.Fingerprint().Equal(o.Fingerprint()) + return LabelSet(m).Equal(LabelSet(o)) } -// Before compares the fingerprints of both metrics. +// Before compares the metrics' underlying label sets. func (m Metric) Before(o Metric) bool { - return m.Fingerprint().Less(o.Fingerprint()) + return LabelSet(m).Before(LabelSet(o)) +} + +// Clone returns a copy of the Metric. +func (m Metric) Clone() Metric { + clone := Metric{} + for k, v := range m { + clone[k] = v + } + return clone } -// String implements Stringer. func (m Metric) String() string { metricName, hasName := m[MetricNameLabel] numLabels := len(m) - 1 @@ -64,69 +71,11 @@ func (m Metric) String() string { // Fingerprint returns a Metric's Fingerprint. func (m Metric) Fingerprint() Fingerprint { - return metricToFingerprint(m) + return LabelSet(m).Fingerprint() } -// Clone returns a copy of the Metric. -func (m Metric) Clone() Metric { - clone := Metric{} - for k, v := range m { - clone[k] = v - } - return clone -} - -// MergeFromLabelSet merges a label set into this Metric, prefixing a collision -// prefix to the label names merged from the label set where required. -func (m Metric) MergeFromLabelSet(labels LabelSet, collisionPrefix LabelName) { - for k, v := range labels { - if collisionPrefix != "" { - for { - if _, exists := m[k]; !exists { - break - } - k = collisionPrefix + k - } - } - - m[k] = v - } -} - -// COWMetric wraps a Metric to enable copy-on-write access patterns. -type COWMetric struct { - Copied bool - Metric Metric -} - -// Set sets a label name in the wrapped Metric to a given value and copies the -// Metric initially, if it is not already a copy. -func (m *COWMetric) Set(ln LabelName, lv LabelValue) { - m.doCOW() - m.Metric[ln] = lv -} - -// Delete deletes a given label name from the wrapped Metric and copies the -// Metric initially, if it is not already a copy. -func (m *COWMetric) Delete(ln LabelName) { - m.doCOW() - delete(m.Metric, ln) -} - -// doCOW copies the underlying Metric if it is not already a copy. -func (m *COWMetric) doCOW() { - if !m.Copied { - m.Metric = m.Metric.Clone() - m.Copied = true - } -} - -// String implements fmt.Stringer. -func (m COWMetric) String() string { - return m.Metric.String() -} - -// MarshalJSON implements json.Marshaler. -func (m COWMetric) MarshalJSON() ([]byte, error) { - return json.Marshal(m.Metric) +// FastFingerprint returns a Metric's Fingerprint calculated by a faster hashing +// algorithm, which is, however, more susceptible to hash collisions. +func (m Metric) FastFingerprint() Fingerprint { + return LabelSet(m).FastFingerprint() } diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go new file mode 100644 index 00000000000..5c7cfceafe9 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go @@ -0,0 +1,83 @@ +// Copyright 2013 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 model + +import "testing" + +func testMetric(t testing.TB) { + var scenarios = []struct { + input LabelSet + fingerprint Fingerprint + fastFingerprint Fingerprint + }{ + { + input: LabelSet{}, + fingerprint: 14695981039346656037, + fastFingerprint: 14695981039346656037, + }, + { + input: LabelSet{ + "first_name": "electro", + "occupation": "robot", + "manufacturer": "westinghouse", + }, + fingerprint: 5911716720268894962, + fastFingerprint: 11310079640881077873, + }, + { + input: LabelSet{ + "x": "y", + }, + fingerprint: 8241431561484471700, + fastFingerprint: 13948396922932177635, + }, + { + input: LabelSet{ + "a": "bb", + "b": "c", + }, + fingerprint: 3016285359649981711, + fastFingerprint: 3198632812309449502, + }, + { + input: LabelSet{ + "a": "b", + "bb": "c", + }, + fingerprint: 7122421792099404749, + fastFingerprint: 5774953389407657638, + }, + } + + for i, scenario := range scenarios { + input := Metric(scenario.input) + + if scenario.fingerprint != input.Fingerprint() { + t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, input.Fingerprint()) + } + if scenario.fastFingerprint != input.FastFingerprint() { + t.Errorf("%d. expected %d, got %d", i, scenario.fastFingerprint, input.FastFingerprint()) + } + } +} + +func TestMetric(t *testing.T) { + testMetric(t) +} + +func BenchmarkMetric(b *testing.B) { + for i := 0; i < b.N; i++ { + testMetric(b) + } +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/model.go b/Godeps/_workspace/src/github.com/prometheus/common/model/model.go similarity index 84% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/model.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/model.go index 189c5dcf6c1..88f013a47a4 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/model.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/model.go @@ -11,5 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package model contains core representation of Prometheus client primitives. +// Package model contains common data structures that are shared across +// Prometheus componenets and libraries. package model diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go b/Godeps/_workspace/src/github.com/prometheus/common/model/signature.go similarity index 58% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/signature.go index cc77b192dd1..28f370065a9 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/signature.go @@ -17,6 +17,7 @@ import ( "bytes" "hash" "hash/fnv" + "sort" "sync" ) @@ -46,36 +47,71 @@ func getHashAndBuf() *hashAndBuf { } func putHashAndBuf(hb *hashAndBuf) { + hb.h.Reset() + hb.b.Reset() hashAndBufPool.Put(hb) } -// LabelsToSignature returns a unique signature (i.e., fingerprint) for a given -// label set. +// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a +// given label set. (Collisions are possible but unlikely if the number of label +// sets the function is applied to is small.) func LabelsToSignature(labels map[string]string) uint64 { if len(labels) == 0 { return emptyLabelSignature } - var result uint64 + labelNames := make([]string, 0, len(labels)) + for labelName := range labels { + labelNames = append(labelNames, labelName) + } + sort.Strings(labelNames) + hb := getHashAndBuf() defer putHashAndBuf(hb) - for labelName, labelValue := range labels { + for _, labelName := range labelNames { hb.b.WriteString(labelName) hb.b.WriteByte(SeparatorByte) - hb.b.WriteString(labelValue) + hb.b.WriteString(labels[labelName]) + hb.b.WriteByte(SeparatorByte) hb.h.Write(hb.b.Bytes()) - result ^= hb.h.Sum64() - hb.h.Reset() hb.b.Reset() } - return result + return hb.h.Sum64() } -// metricToFingerprint works exactly as LabelsToSignature but takes a Metric as +// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as // parameter (rather than a label map) and returns a Fingerprint. -func metricToFingerprint(m Metric) Fingerprint { - if len(m) == 0 { +func labelSetToFingerprint(ls LabelSet) Fingerprint { + if len(ls) == 0 { + return Fingerprint(emptyLabelSignature) + } + + labelNames := make(LabelNames, 0, len(ls)) + for labelName := range ls { + labelNames = append(labelNames, labelName) + } + sort.Sort(labelNames) + + hb := getHashAndBuf() + defer putHashAndBuf(hb) + + for _, labelName := range labelNames { + hb.b.WriteString(string(labelName)) + hb.b.WriteByte(SeparatorByte) + hb.b.WriteString(string(ls[labelName])) + hb.b.WriteByte(SeparatorByte) + hb.h.Write(hb.b.Bytes()) + hb.b.Reset() + } + return Fingerprint(hb.h.Sum64()) +} + +// labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a +// faster and less allocation-heavy hash function, which is more susceptible to +// create hash collisions. Therefore, collision detection should be applied. +func labelSetToFastFingerprint(ls LabelSet) Fingerprint { + if len(ls) == 0 { return Fingerprint(emptyLabelSignature) } @@ -83,7 +119,7 @@ func metricToFingerprint(m Metric) Fingerprint { hb := getHashAndBuf() defer putHashAndBuf(hb) - for labelName, labelValue := range m { + for labelName, labelValue := range ls { hb.b.WriteString(string(labelName)) hb.b.WriteByte(SeparatorByte) hb.b.WriteString(string(labelValue)) @@ -97,13 +133,15 @@ func metricToFingerprint(m Metric) Fingerprint { // SignatureForLabels works like LabelsToSignature but takes a Metric as // parameter (rather than a label map) and only includes the labels with the -// specified LabelNames into the signature calculation. -func SignatureForLabels(m Metric, labels LabelNames) uint64 { +// specified LabelNames into the signature calculation. The labels passed in +// will be sorted by this function. +func SignatureForLabels(m Metric, labels ...LabelName) uint64 { if len(m) == 0 || len(labels) == 0 { return emptyLabelSignature } - var result uint64 + sort.Sort(LabelNames(labels)) + hb := getHashAndBuf() defer putHashAndBuf(hb) @@ -111,12 +149,11 @@ func SignatureForLabels(m Metric, labels LabelNames) uint64 { hb.b.WriteString(string(label)) hb.b.WriteByte(SeparatorByte) hb.b.WriteString(string(m[label])) + hb.b.WriteByte(SeparatorByte) hb.h.Write(hb.b.Bytes()) - result ^= hb.h.Sum64() - hb.h.Reset() hb.b.Reset() } - return result + return hb.h.Sum64() } // SignatureWithoutLabels works like LabelsToSignature but takes a Metric as @@ -127,24 +164,27 @@ func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 { return emptyLabelSignature } - var result uint64 + labelNames := make(LabelNames, 0, len(m)) + for labelName := range m { + if _, exclude := labels[labelName]; !exclude { + labelNames = append(labelNames, labelName) + } + } + if len(labelNames) == 0 { + return emptyLabelSignature + } + sort.Sort(labelNames) + hb := getHashAndBuf() defer putHashAndBuf(hb) - for labelName, labelValue := range m { - if _, exclude := labels[labelName]; exclude { - continue - } + for _, labelName := range labelNames { hb.b.WriteString(string(labelName)) hb.b.WriteByte(SeparatorByte) - hb.b.WriteString(string(labelValue)) + hb.b.WriteString(string(m[labelName])) + hb.b.WriteByte(SeparatorByte) hb.h.Write(hb.b.Bytes()) - result ^= hb.h.Sum64() - hb.h.Reset() hb.b.Reset() } - if result == 0 { - return emptyLabelSignature - } - return result + return hb.h.Sum64() } diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go similarity index 55% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go index 7b3327d4406..d9c665f8c79 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/signature_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go @@ -30,7 +30,7 @@ func TestLabelsToSignature(t *testing.T) { }, { in: map[string]string{"name": "garland, briggs", "fear": "love is not enough"}, - out: 12952432476264840823, + out: 5799056148416392346, }, } @@ -45,21 +45,45 @@ func TestLabelsToSignature(t *testing.T) { func TestMetricToFingerprint(t *testing.T) { var scenarios = []struct { - in Metric + in LabelSet out Fingerprint }{ { - in: Metric{}, + in: LabelSet{}, out: 14695981039346656037, }, { - in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, + in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"}, + out: 5799056148416392346, + }, + } + + for i, scenario := range scenarios { + actual := labelSetToFingerprint(scenario.in) + + if actual != scenario.out { + t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) + } + } +} + +func TestMetricToFastFingerprint(t *testing.T) { + var scenarios = []struct { + in LabelSet + out Fingerprint + }{ + { + in: LabelSet{}, + out: 14695981039346656037, + }, + { + in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"}, out: 12952432476264840823, }, } for i, scenario := range scenarios { - actual := metricToFingerprint(scenario.in) + actual := labelSetToFastFingerprint(scenario.in) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) @@ -81,12 +105,12 @@ func TestSignatureForLabels(t *testing.T) { { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: LabelNames{"fear", "name"}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, labels: LabelNames{"fear", "name"}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, @@ -101,7 +125,7 @@ func TestSignatureForLabels(t *testing.T) { } for i, scenario := range scenarios { - actual := SignatureForLabels(scenario.in, scenario.labels) + actual := SignatureForLabels(scenario.in, scenario.labels...) if actual != scenario.out { t.Errorf("%d. expected %d, got %d", i, scenario.out, actual) @@ -128,17 +152,17 @@ func TestSignatureWithoutLabels(t *testing.T) { { in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"}, labels: map[LabelName]struct{}{"foo": struct{}{}}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: map[LabelName]struct{}{}, - out: 12952432476264840823, + out: 5799056148416392346, }, { in: Metric{"name": "garland, briggs", "fear": "love is not enough"}, labels: nil, - out: 12952432476264840823, + out: 5799056148416392346, }, } @@ -164,21 +188,21 @@ func BenchmarkLabelToSignatureScalar(b *testing.B) { } func BenchmarkLabelToSignatureSingle(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5147259542624943964) + benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5146282821936882169) } func BenchmarkLabelToSignatureDouble(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) + benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) } func BenchmarkLabelToSignatureTriple(b *testing.B) { - benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) + benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) } -func benchmarkMetricToFingerprint(b *testing.B, m Metric, e Fingerprint) { +func benchmarkMetricToFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { for i := 0; i < b.N; i++ { - if a := metricToFingerprint(m); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, m, a) + if a := labelSetToFingerprint(ls); a != e { + b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) } } } @@ -188,18 +212,42 @@ func BenchmarkMetricToFingerprintScalar(b *testing.B) { } func BenchmarkMetricToFingerprintSingle(b *testing.B) { - benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value"}, 5147259542624943964) + benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5146282821936882169) } func BenchmarkMetricToFingerprintDouble(b *testing.B) { - benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) + benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717) } func BenchmarkMetricToFingerprintTriple(b *testing.B) { - benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) + benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121) } -func TestEmptyLabelSignature(t *testing.T) { +func benchmarkMetricToFastFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { + for i := 0; i < b.N; i++ { + if a := labelSetToFastFingerprint(ls); a != e { + b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) + } + } +} + +func BenchmarkMetricToFastFingerprintScalar(b *testing.B) { + benchmarkMetricToFastFingerprint(b, nil, 14695981039346656037) +} + +func BenchmarkMetricToFastFingerprintSingle(b *testing.B) { + benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5147259542624943964) +} + +func BenchmarkMetricToFastFingerprintDouble(b *testing.B) { + benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528) +} + +func BenchmarkMetricToFastFingerprintTriple(b *testing.B) { + benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676) +} + +func BenchmarkEmptyLabelSignature(b *testing.B) { input := []map[string]string{nil, {}} var ms runtime.MemStats @@ -214,11 +262,11 @@ func TestEmptyLabelSignature(t *testing.T) { runtime.ReadMemStats(&ms) if got := ms.Alloc; alloc != got { - t.Fatal("expected LabelsToSignature with empty labels not to perform allocations") + b.Fatal("expected LabelsToSignature with empty labels not to perform allocations") } } -func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, concLevel int) { +func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerprint, concLevel int) { var start, end sync.WaitGroup start.Add(1) end.Add(concLevel) @@ -227,8 +275,8 @@ func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, con go func() { start.Wait() for j := b.N / concLevel; j >= 0; j-- { - if a := metricToFingerprint(m); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, m, a) + if a := labelSetToFastFingerprint(ls); a != e { + b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) } } end.Done() @@ -239,18 +287,18 @@ func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, con end.Wait() } -func BenchmarkMetricToFingerprintTripleConc1(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1) +func BenchmarkMetricToFastFingerprintTripleConc1(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1) } -func BenchmarkMetricToFingerprintTripleConc2(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2) +func BenchmarkMetricToFastFingerprintTripleConc2(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2) } -func BenchmarkMetricToFingerprintTripleConc4(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4) +func BenchmarkMetricToFastFingerprintTripleConc4(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4) } -func BenchmarkMetricToFingerprintTripleConc8(b *testing.B) { - benchmarkMetricToFingerprintConc(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8) +func BenchmarkMetricToFastFingerprintTripleConc8(b *testing.B) { + benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8) } diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/time.go b/Godeps/_workspace/src/github.com/prometheus/common/model/time.go new file mode 100644 index 00000000000..ebc8bf6cc84 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/time.go @@ -0,0 +1,230 @@ +// Copyright 2013 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 model + +import ( + "fmt" + "math" + "regexp" + "strconv" + "strings" + "time" +) + +const ( + // MinimumTick is the minimum supported time resolution. This has to be + // at least time.Second in order for the code below to work. + minimumTick = time.Millisecond + // second is the Time duration equivalent to one second. + second = int64(time.Second / minimumTick) + // The number of nanoseconds per minimum tick. + nanosPerTick = int64(minimumTick / time.Nanosecond) + + // Earliest is the earliest Time representable. Handy for + // initializing a high watermark. + Earliest = Time(math.MinInt64) + // Latest is the latest Time representable. Handy for initializing + // a low watermark. + Latest = Time(math.MaxInt64) +) + +// Time is the number of milliseconds since the epoch +// (1970-01-01 00:00 UTC) excluding leap seconds. +type Time int64 + +// Interval describes and interval between two timestamps. +type Interval struct { + Start, End Time +} + +// Now returns the current time as a Time. +func Now() Time { + return TimeFromUnixNano(time.Now().UnixNano()) +} + +// TimeFromUnix returns the Time equivalent to the Unix Time t +// provided in seconds. +func TimeFromUnix(t int64) Time { + return Time(t * second) +} + +// TimeFromUnixNano returns the Time equivalent to the Unix Time +// t provided in nanoseconds. +func TimeFromUnixNano(t int64) Time { + return Time(t / nanosPerTick) +} + +// Equal reports whether two Times represent the same instant. +func (t Time) Equal(o Time) bool { + return t == o +} + +// Before reports whether the Time t is before o. +func (t Time) Before(o Time) bool { + return t < o +} + +// After reports whether the Time t is after o. +func (t Time) After(o Time) bool { + return t > o +} + +// Add returns the Time t + d. +func (t Time) Add(d time.Duration) Time { + return t + Time(d/minimumTick) +} + +// Sub returns the Duration t - o. +func (t Time) Sub(o Time) time.Duration { + return time.Duration(t-o) * minimumTick +} + +// Time returns the time.Time representation of t. +func (t Time) Time() time.Time { + return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick) +} + +// Unix returns t as a Unix time, the number of seconds elapsed +// since January 1, 1970 UTC. +func (t Time) Unix() int64 { + return int64(t) / second +} + +// UnixNano returns t as a Unix time, the number of nanoseconds elapsed +// since January 1, 1970 UTC. +func (t Time) UnixNano() int64 { + return int64(t) * nanosPerTick +} + +// The number of digits after the dot. +var dotPrecision = int(math.Log10(float64(second))) + +// String returns a string representation of the Time. +func (t Time) String() string { + return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64) +} + +// MarshalJSON implements the json.Marshaler interface. +func (t Time) MarshalJSON() ([]byte, error) { + return []byte(t.String()), nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (t *Time) UnmarshalJSON(b []byte) error { + p := strings.Split(string(b), ".") + switch len(p) { + case 1: + v, err := strconv.ParseInt(string(p[0]), 10, 64) + if err != nil { + return err + } + *t = Time(v * second) + + case 2: + v, err := strconv.ParseInt(string(p[0]), 10, 64) + if err != nil { + return err + } + v *= second + + prec := dotPrecision - len(p[1]) + if prec < 0 { + p[1] = p[1][:dotPrecision] + } else if prec > 0 { + p[1] = p[1] + strings.Repeat("0", prec) + } + + va, err := strconv.ParseInt(p[1], 10, 32) + if err != nil { + return err + } + + *t = Time(v + va) + + default: + return fmt.Errorf("invalid time %q", string(b)) + } + return nil +} + +// Duration wraps time.Duration. It is used to parse the custom duration format +// from YAML. +// This type should not propagate beyond the scope of input/output processing. +type Duration time.Duration + +// StringToDuration parses a string into a time.Duration, assuming that a year +// a day always has 24h. +func ParseDuration(durationStr string) (Duration, error) { + matches := durationRE.FindStringSubmatch(durationStr) + if len(matches) != 3 { + return 0, fmt.Errorf("not a valid duration string: %q", durationStr) + } + durSeconds, _ := strconv.Atoi(matches[1]) + dur := time.Duration(durSeconds) * time.Second + unit := matches[2] + switch unit { + case "d": + dur *= 60 * 60 * 24 + case "h": + dur *= 60 * 60 + case "m": + dur *= 60 + case "s": + dur *= 1 + default: + return 0, fmt.Errorf("invalid time unit in duration string: %q", unit) + } + return Duration(dur), nil +} + +var durationRE = regexp.MustCompile("^([0-9]+)([ywdhms]+)$") + +func (d Duration) String() string { + seconds := int64(time.Duration(d) / time.Second) + factors := map[string]int64{ + "d": 60 * 60 * 24, + "h": 60 * 60, + "m": 60, + "s": 1, + } + unit := "s" + switch int64(0) { + case seconds % factors["d"]: + unit = "d" + case seconds % factors["h"]: + unit = "h" + case seconds % factors["m"]: + unit = "m" + } + return fmt.Sprintf("%v%v", seconds/factors[unit], unit) +} + +// MarshalYAML implements the yaml.Marshaler interface. +func (d Duration) MarshalYAML() (interface{}, error) { + return d.String(), nil +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + if err := unmarshal(&s); err != nil { + return err + } + dur, err := ParseDuration(s) + if err != nil { + return err + } + *d = dur + return nil +} diff --git a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/timestamp_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go similarity index 77% rename from Godeps/_workspace/src/github.com/prometheus/client_golang/model/timestamp_test.go rename to Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go index fa028a47deb..9013a62775b 100644 --- a/Godeps/_workspace/src/github.com/prometheus/client_golang/model/timestamp_test.go +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go @@ -15,13 +15,13 @@ package model import ( "testing" - native_time "time" + "time" ) func TestComparators(t *testing.T) { - t1a := TimestampFromUnix(0) - t1b := TimestampFromUnix(0) - t2 := TimestampFromUnix(2*second - 1) + t1a := TimeFromUnix(0) + t1b := TimeFromUnix(0) + t2 := TimeFromUnix(2*second - 1) if !t1a.Equal(t1b) { t.Fatalf("Expected %s to be equal to %s", t1a, t1b) @@ -45,21 +45,21 @@ func TestComparators(t *testing.T) { } } -func TestTimestampConversions(t *testing.T) { +func TestTimeConversions(t *testing.T) { unixSecs := int64(1136239445) unixNsecs := int64(123456789) - unixNano := unixSecs*1000000000 + unixNsecs + unixNano := unixSecs*1e9 + unixNsecs - t1 := native_time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick) - t2 := native_time.Unix(unixSecs, unixNsecs) + t1 := time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick) + t2 := time.Unix(unixSecs, unixNsecs) - ts := TimestampFromUnixNano(unixNano) + ts := TimeFromUnixNano(unixNano) if !ts.Time().Equal(t1) { t.Fatalf("Expected %s, got %s", t1, ts.Time()) } // Test available precision. - ts = TimestampFromTime(t2) + ts = TimeFromUnixNano(t2.UnixNano()) if !ts.Time().Equal(t1) { t.Fatalf("Expected %s, got %s", t1, ts.Time()) } @@ -70,10 +70,10 @@ func TestTimestampConversions(t *testing.T) { } func TestDuration(t *testing.T) { - duration := native_time.Second + native_time.Minute + native_time.Hour - goTime := native_time.Unix(1136239445, 0) + duration := time.Second + time.Minute + time.Hour + goTime := time.Unix(1136239445, 0) - ts := TimestampFromTime(goTime) + ts := TimeFromUnix(goTime.Unix()) if !goTime.Add(duration).Equal(ts.Add(duration).Time()) { t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration)) } diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go new file mode 100644 index 00000000000..10ffb0bd611 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/value.go @@ -0,0 +1,395 @@ +// Copyright 2013 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 model + +import ( + "encoding/json" + "fmt" + "sort" + "strconv" + "strings" +) + +// A SampleValue is a representation of a value for a given sample at a given +// time. +type SampleValue float64 + +// MarshalJSON implements json.Marshaler. +func (v SampleValue) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (v *SampleValue) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("sample value must be a quoted string") + } + f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) + if err != nil { + return err + } + *v = SampleValue(f) + return nil +} + +func (v SampleValue) Equal(o SampleValue) bool { + return v == o +} + +func (v SampleValue) String() string { + return strconv.FormatFloat(float64(v), 'f', -1, 64) +} + +// SamplePair pairs a SampleValue with a Timestamp. +type SamplePair struct { + Timestamp Time + Value SampleValue +} + +// MarshalJSON implements json.Marshaler. +func (s SamplePair) MarshalJSON() ([]byte, error) { + t, err := json.Marshal(s.Timestamp) + if err != nil { + return nil, err + } + v, err := json.Marshal(s.Value) + if err != nil { + return nil, err + } + return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *SamplePair) UnmarshalJSON(b []byte) error { + v := [...]json.Unmarshaler{&s.Timestamp, &s.Value} + return json.Unmarshal(b, &v) +} + +// Equal returns true if this SamplePair and o have equal Values and equal +// Timestamps. +func (s *SamplePair) Equal(o *SamplePair) bool { + return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp)) +} + +func (s SamplePair) String() string { + return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp) +} + +// Sample is a sample pair associated with a metric. +type Sample struct { + Metric Metric `json:"metric"` + Value SampleValue `json:"value"` + Timestamp Time `json:"timestamp"` +} + +// Equal compares first the metrics, then the timestamp, then the value. +func (s *Sample) Equal(o *Sample) bool { + if s == o { + return true + } + + if !s.Metric.Equal(o.Metric) { + return false + } + if !s.Timestamp.Equal(o.Timestamp) { + return false + } + if s.Value != o.Value { + return false + } + + return true +} + +func (s Sample) String() string { + return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ + Timestamp: s.Timestamp, + Value: s.Value, + }) +} + +// MarshalJSON implements json.Marshaler. +func (s Sample) MarshalJSON() ([]byte, error) { + v := struct { + Metric Metric `json:"metric"` + Value SamplePair `json:"value"` + }{ + Metric: s.Metric, + Value: SamplePair{ + Timestamp: s.Timestamp, + Value: s.Value, + }, + } + + return json.Marshal(&v) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *Sample) UnmarshalJSON(b []byte) error { + v := struct { + Metric Metric `json:"metric"` + Value SamplePair `json:"value"` + }{ + Metric: s.Metric, + Value: SamplePair{ + Timestamp: s.Timestamp, + Value: s.Value, + }, + } + + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + s.Metric = v.Metric + s.Timestamp = v.Value.Timestamp + s.Value = v.Value.Value + + return nil +} + +// Samples is a sortable Sample slice. It implements sort.Interface. +type Samples []*Sample + +func (s Samples) Len() int { + return len(s) +} + +// Less compares first the metrics, then the timestamp. +func (s Samples) Less(i, j int) bool { + switch { + case s[i].Metric.Before(s[j].Metric): + return true + case s[j].Metric.Before(s[i].Metric): + return false + case s[i].Timestamp.Before(s[j].Timestamp): + return true + default: + return false + } +} + +func (s Samples) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Equal compares two sets of samples and returns true if they are equal. +func (s Samples) Equal(o Samples) bool { + if len(s) != len(o) { + return false + } + + for i, sample := range s { + if !sample.Equal(o[i]) { + return false + } + } + return true +} + +// SampleStream is a stream of Values belonging to an attached COWMetric. +type SampleStream struct { + Metric Metric `json:"metric"` + Values []SamplePair `json:"values"` +} + +func (ss SampleStream) String() string { + vals := make([]string, len(ss.Values)) + for i, v := range ss.Values { + vals[i] = v.String() + } + return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) +} + +// Value is a generic interface for values resulting from a query evaluation. +type Value interface { + Type() ValueType + String() string +} + +func (Matrix) Type() ValueType { return ValMatrix } +func (Vector) Type() ValueType { return ValVector } +func (*Scalar) Type() ValueType { return ValScalar } +func (*String) Type() ValueType { return ValString } + +type ValueType int + +const ( + ValNone ValueType = iota + ValScalar + ValVector + ValMatrix + ValString +) + +// MarshalJSON implements json.Marshaler. +func (et ValueType) MarshalJSON() ([]byte, error) { + return json.Marshal(et.String()) +} + +func (et *ValueType) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + switch s { + case "": + *et = ValNone + case "scalar": + *et = ValScalar + case "vector": + *et = ValVector + case "matrix": + *et = ValMatrix + case "string": + *et = ValString + default: + return fmt.Errorf("unknown value type %q", s) + } + return nil +} + +func (e ValueType) String() string { + switch e { + case ValNone: + return "" + case ValScalar: + return "scalar" + case ValVector: + return "vector" + case ValMatrix: + return "matrix" + case ValString: + return "string" + } + panic("ValueType.String: unhandled value type") +} + +// Scalar is a scalar value evaluated at the set timestamp. +type Scalar struct { + Value SampleValue `json:"value"` + Timestamp Time `json:"timestamp"` +} + +func (s Scalar) String() string { + return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp) +} + +// MarshalJSON implements json.Marshaler. +func (s Scalar) MarshalJSON() ([]byte, error) { + v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) + return json.Marshal([...]interface{}{s.Timestamp, string(v)}) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *Scalar) UnmarshalJSON(b []byte) error { + var f string + v := [...]interface{}{&s.Timestamp, &f} + + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + value, err := strconv.ParseFloat(f, 64) + if err != nil { + return fmt.Errorf("error parsing sample value: %s", err) + } + s.Value = SampleValue(value) + return nil +} + +// String is a string value evaluated at the set timestamp. +type String struct { + Value string `json:"value"` + Timestamp Time `json:"timestamp"` +} + +func (s *String) String() string { + return s.Value +} + +// MarshalJSON implements json.Marshaler. +func (s String) MarshalJSON() ([]byte, error) { + return json.Marshal([]interface{}{s.Timestamp, s.Value}) +} + +// UnmarshalJSON implements json.Unmarshaler. +func (s *String) UnmarshalJSON(b []byte) error { + v := [...]interface{}{&s.Timestamp, &s.Value} + return json.Unmarshal(b, &v) +} + +// Vector is basically only an alias for Samples, but the +// contract is that in a Vector, all Samples have the same timestamp. +type Vector []*Sample + +func (vec Vector) String() string { + entries := make([]string, len(vec)) + for i, s := range vec { + entries[i] = s.String() + } + return strings.Join(entries, "\n") +} + +func (vec Vector) Len() int { return len(vec) } +func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] } + +// Less compares first the metrics, then the timestamp. +func (vec Vector) Less(i, j int) bool { + switch { + case vec[i].Metric.Before(vec[j].Metric): + return true + case vec[j].Metric.Before(vec[i].Metric): + return false + case vec[i].Timestamp.Before(vec[j].Timestamp): + return true + default: + return false + } +} + +// Equal compares two sets of samples and returns true if they are equal. +func (vec Vector) Equal(o Vector) bool { + if len(vec) != len(o) { + return false + } + + for i, sample := range vec { + if !sample.Equal(o[i]) { + return false + } + } + return true +} + +// Matrix is a list of time series. +type Matrix []*SampleStream + +func (m Matrix) Len() int { return len(m) } +func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) } +func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] } + +func (mat Matrix) String() string { + matCp := make(Matrix, len(mat)) + copy(matCp, mat) + sort.Sort(matCp) + + strs := make([]string, len(matCp)) + + for i, ss := range matCp { + strs[i] = ss.String() + } + + return strings.Join(strs, "\n") +} diff --git a/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go b/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go new file mode 100644 index 00000000000..2e9c7eb09d1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go @@ -0,0 +1,362 @@ +// Copyright 2013 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 model + +import ( + "encoding/json" + "math" + "reflect" + "sort" + "testing" +) + +func TestSamplePairJSON(t *testing.T) { + input := []struct { + plain string + value SamplePair + }{ + { + plain: `[1234.567,"123.1"]`, + value: SamplePair{ + Value: 123.1, + Timestamp: 1234567, + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sp SamplePair + err = json.Unmarshal(b, &sp) + if err != nil { + t.Error(err) + continue + } + + if sp != test.value { + t.Errorf("decoding error: expected %v, got %v", test.value, sp) + } + } +} + +func TestSampleJSON(t *testing.T) { + input := []struct { + plain string + value Sample + }{ + { + plain: `{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}`, + value: Sample{ + Metric: Metric{ + MetricNameLabel: "test_metric", + }, + Value: 123.1, + Timestamp: 1234567, + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sv Sample + err = json.Unmarshal(b, &sv) + if err != nil { + t.Error(err) + continue + } + + if !reflect.DeepEqual(sv, test.value) { + t.Errorf("decoding error: expected %v, got %v", test.value, sv) + } + } +} + +func TestVectorJSON(t *testing.T) { + input := []struct { + plain string + value Vector + }{ + { + plain: `[]`, + value: Vector{}, + }, + { + plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}]`, + value: Vector{&Sample{ + Metric: Metric{ + MetricNameLabel: "test_metric", + }, + Value: 123.1, + Timestamp: 1234567, + }}, + }, + { + plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]},{"metric":{"foo":"bar"},"value":[1.234,"+Inf"]}]`, + value: Vector{ + &Sample{ + Metric: Metric{ + MetricNameLabel: "test_metric", + }, + Value: 123.1, + Timestamp: 1234567, + }, + &Sample{ + Metric: Metric{ + "foo": "bar", + }, + Value: SampleValue(math.Inf(1)), + Timestamp: 1234, + }, + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var vec Vector + err = json.Unmarshal(b, &vec) + if err != nil { + t.Error(err) + continue + } + + if !reflect.DeepEqual(vec, test.value) { + t.Errorf("decoding error: expected %v, got %v", test.value, vec) + } + } +} + +func TestScalarJSON(t *testing.T) { + input := []struct { + plain string + value Scalar + }{ + { + plain: `[123.456,"456"]`, + value: Scalar{ + Timestamp: 123456, + Value: 456, + }, + }, + { + plain: `[123123.456,"+Inf"]`, + value: Scalar{ + Timestamp: 123123456, + Value: SampleValue(math.Inf(1)), + }, + }, + { + plain: `[123123.456,"-Inf"]`, + value: Scalar{ + Timestamp: 123123456, + Value: SampleValue(math.Inf(-1)), + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sv Scalar + err = json.Unmarshal(b, &sv) + if err != nil { + t.Error(err) + continue + } + + if sv != test.value { + t.Errorf("decoding error: expected %v, got %v", test.value, sv) + } + } +} + +func TestStringJSON(t *testing.T) { + input := []struct { + plain string + value String + }{ + { + plain: `[123.456,"test"]`, + value: String{ + Timestamp: 123456, + Value: "test", + }, + }, + { + plain: `[123123.456,"台北"]`, + value: String{ + Timestamp: 123123456, + Value: "台北", + }, + }, + } + + for _, test := range input { + b, err := json.Marshal(test.value) + if err != nil { + t.Error(err) + continue + } + + if string(b) != test.plain { + t.Errorf("encoding error: expected %q, got %q", test.plain, b) + continue + } + + var sv String + err = json.Unmarshal(b, &sv) + if err != nil { + t.Error(err) + continue + } + + if sv != test.value { + t.Errorf("decoding error: expected %v, got %v", test.value, sv) + } + } +} + +func TestVectorSort(t *testing.T) { + input := Vector{ + &Sample{ + Metric: Metric{ + MetricNameLabel: "A", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "A", + }, + Timestamp: 2, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "C", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "C", + }, + Timestamp: 2, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "B", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "B", + }, + Timestamp: 2, + }, + } + + expected := Vector{ + &Sample{ + Metric: Metric{ + MetricNameLabel: "A", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "A", + }, + Timestamp: 2, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "B", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "B", + }, + Timestamp: 2, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "C", + }, + Timestamp: 1, + }, + &Sample{ + Metric: Metric{ + MetricNameLabel: "C", + }, + Timestamp: 2, + }, + } + + sort.Sort(input) + + for i, actual := range input { + actualFp := actual.Metric.Fingerprint() + expectedFp := expected[i].Metric.Fingerprint() + + if actualFp != expectedFp { + t.Fatalf("%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String()) + } + + if actual.Timestamp != expected[i].Timestamp { + t.Fatalf("%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp) + } + } +} diff --git a/hack/verify-godeps.sh b/hack/verify-godeps.sh index 325836b1dfe..0787e49bf3b 100755 --- a/hack/verify-godeps.sh +++ b/hack/verify-godeps.sh @@ -62,10 +62,6 @@ GODEP="${_tmpdir}/bin/godep" # fill out that nice clean place with the kube godeps echo "Starting to download all kubernetes godeps. This takes a while" -# github.com/prometheus/client_golang removed the model and extraction directory. -# thus go get fails and thus godep fails. So load it by hand. -preload-dep "github.com/prometheus" "client_golang" "692492e54b553a81013254cc1fba4b6dd76fad30" - "${GODEP}" restore echo "Download finished" diff --git a/test/e2e/kubelet_stats.go b/test/e2e/kubelet_stats.go index e688e460006..b75f812f916 100644 --- a/test/e2e/kubelet_stats.go +++ b/test/e2e/kubelet_stats.go @@ -30,6 +30,7 @@ import ( "time" cadvisor "github.com/google/cadvisor/info/v1" + "github.com/prometheus/common/model" "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/fields" @@ -39,9 +40,6 @@ import ( "k8s.io/kubernetes/pkg/master/ports" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/sets" - - "github.com/prometheus/client_golang/extraction" - "github.com/prometheus/client_golang/model" ) // KubeletMetric stores metrics scraped from the kubelet server's /metric endpoint. @@ -64,10 +62,13 @@ func (a KubeletMetricByLatency) Len() int { return len(a) } func (a KubeletMetricByLatency) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a KubeletMetricByLatency) Less(i, j int) bool { return a[i].Latency > a[j].Latency } -// KubeletMetricIngester implements extraction.Ingester -type kubeletMetricIngester []KubeletMetric +// ParseKubeletMetrics reads metrics from the kubelet server running on the given node +func ParseKubeletMetrics(metricsBlob string) ([]KubeletMetric, error) { + samples, err := extractMetricSamples(metricsBlob) + if err != nil { + return nil, err + } -func (k *kubeletMetricIngester) Ingest(samples model.Samples) error { acceptedMethods := sets.NewString( metrics.PodWorkerLatencyKey, metrics.PodWorkerStartLatencyKey, @@ -79,6 +80,7 @@ func (k *kubeletMetricIngester) Ingest(samples model.Samples) error { metrics.DockerErrorsKey, ) + var kms []KubeletMetric for _, sample := range samples { const prefix = metrics.KubeletSubsystem + "_" metricName := string(sample.Metric[model.MetricNameLabel]) @@ -106,16 +108,14 @@ func (k *kubeletMetricIngester) Ingest(samples model.Samples) error { } } - *k = append(*k, KubeletMetric{operation, method, quantile, time.Duration(int64(latency)) * time.Microsecond}) + kms = append(kms, KubeletMetric{ + operation, + method, + quantile, + time.Duration(int64(latency)) * time.Microsecond, + }) } - return nil -} - -// ReadKubeletMetrics reads metrics from the kubelet server running on the given node -func ParseKubeletMetrics(metricsBlob string) ([]KubeletMetric, error) { - var ingester kubeletMetricIngester - err := extraction.Processor004.ProcessSingle(strings.NewReader(metricsBlob), &ingester, &extraction.ProcessOptions{}) - return ingester, err + return kms, nil } // HighLatencyKubeletOperations logs and counts the high latency metrics exported by the kubelet server via /metrics. diff --git a/test/e2e/metrics_util.go b/test/e2e/metrics_util.go index 7620365d5ea..31af49b1425 100644 --- a/test/e2e/metrics_util.go +++ b/test/e2e/metrics_util.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "fmt" + "io" "io/ioutil" "net/http" "os" @@ -31,8 +32,8 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/util/sets" - "github.com/prometheus/client_golang/extraction" - "github.com/prometheus/client_golang/model" + "github.com/prometheus/common/expfmt" + "github.com/prometheus/common/model" ) // Dashboard metrics @@ -62,34 +63,6 @@ func (a APIResponsiveness) Less(i, j int) bool { return a.APICalls[i].Latency.Perc99 < a.APICalls[j].Latency.Perc99 } -// Ingest method implements extraction.Ingester (necessary for Prometheus library -// to parse the metrics). -func (a *APIResponsiveness) Ingest(samples model.Samples) error { - ignoredResources := sets.NewString("events") - ignoredVerbs := sets.NewString("WATCHLIST", "PROXY") - - for _, sample := range samples { - // Example line: - // apiserver_request_latencies_summary{resource="namespaces",verb="LIST",quantile="0.99"} 908 - if sample.Metric[model.MetricNameLabel] != "apiserver_request_latencies_summary" { - continue - } - - resource := string(sample.Metric["resource"]) - verb := string(sample.Metric["verb"]) - if ignoredResources.Has(resource) || ignoredVerbs.Has(verb) { - continue - } - latency := sample.Value - quantile, err := strconv.ParseFloat(string(sample.Metric[model.QuantileLabel]), 64) - if err != nil { - return err - } - a.addMetric(resource, verb, quantile, time.Duration(int64(latency))*time.Microsecond) - } - return nil -} - // 0 <= quantile <=1 (e.g. 0.95 is 95%tile, 0.5 is median) // Only 0.5, 0.9 and 0.99 quantiles are supported. func (a *APIResponsiveness) addMetric(resource, verb string, quantile float64, latency time.Duration) { @@ -118,14 +91,42 @@ func setQuantile(apicall APICall, quantile float64, latency time.Duration) APICa } func readLatencyMetrics(c *client.Client) (APIResponsiveness, error) { + var a APIResponsiveness + body, err := getMetrics(c) if err != nil { - return APIResponsiveness{}, err + return a, err } - var ingester APIResponsiveness - err = extraction.Processor004.ProcessSingle(strings.NewReader(body), &ingester, &extraction.ProcessOptions{}) - return ingester, err + samples, err := extractMetricSamples(body) + if err != nil { + return a, err + } + + ignoredResources := sets.NewString("events") + ignoredVerbs := sets.NewString("WATCHLIST", "PROXY") + + for _, sample := range samples { + // Example line: + // apiserver_request_latencies_summary{resource="namespaces",verb="LIST",quantile="0.99"} 908 + if sample.Metric[model.MetricNameLabel] != "apiserver_request_latencies_summary" { + continue + } + + resource := string(sample.Metric["resource"]) + verb := string(sample.Metric["verb"]) + if ignoredResources.Has(resource) || ignoredVerbs.Has(verb) { + continue + } + latency := sample.Value + quantile, err := strconv.ParseFloat(string(sample.Metric[model.QuantileLabel]), 64) + if err != nil { + return a, err + } + a.addMetric(resource, verb, quantile, time.Duration(int64(latency))*time.Microsecond) + } + + return a, err } // Prints summary metrics for request types with latency above threshold @@ -275,3 +276,28 @@ func writePerfData(c *client.Client, dirName string, postfix string) error { } return nil } + +// extractMetricSamples parses the prometheus metric samples from the input string. +func extractMetricSamples(metricsBlob string) ([]*model.Sample, error) { + dec, err := expfmt.NewDecoder(strings.NewReader(metricsBlob), expfmt.FmtText) + if err != nil { + return nil, err + } + decoder := expfmt.SampleDecoder{ + Dec: dec, + Opts: &expfmt.DecodeOptions{}, + } + + var samples []*model.Sample + for { + var v model.Vector + if err = decoder.Decode(&v); err != nil { + if err == io.EOF { + // Expected loop termination condition. + return samples, nil + } + return nil, err + } + samples = append(samples, v...) + } +} diff --git a/test/integration/metrics_test.go b/test/integration/metrics_test.go index d3dc6945c78..b974539464a 100644 --- a/test/integration/metrics_test.go +++ b/test/integration/metrics_test.go @@ -97,7 +97,7 @@ func TestMasterProcessMetrics(t *testing.T) { checkForExpectedMetrics(t, metrics, []string{ "process_start_time_seconds", "process_cpu_seconds_total", - "process_goroutines", + "go_goroutines", "process_open_fds", "process_resident_memory_bytes", })