Revert "Update prometheus library usage"

pull/6/head
Marek Grabowski 2015-09-19 22:18:15 +02:00
parent a2dcd3b78d
commit 98a971cc2e
99 changed files with 3800 additions and 4127 deletions

27
Godeps/Godeps.json generated
View File

@ -454,24 +454,31 @@
"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.7.0-39-g3b78d7a",
"Rev": "3b78d7a77f51ccbc364d4bc170920153022cfd08"
"Comment": "0.4.0-1-g692492e",
"Rev": "692492e54b553a81013254cc1fba4b6dd76fad30"
},
{
"ImportPath": "github.com/prometheus/client_golang/text",
"Comment": "0.4.0-1-g692492e",
"Rev": "692492e54b553a81013254cc1fba4b6dd76fad30"
},
{
"ImportPath": "github.com/prometheus/client_model/go",
"Comment": "model-0.0.2-12-gfa8ad6f",
"Rev": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6"
},
{
"ImportPath": "github.com/prometheus/common/expfmt",
"Rev": "53cc5b7aea4df4e69c1b9d71fca97d43b416a3ad"
},
{
"ImportPath": "github.com/prometheus/common/model",
"Rev": "53cc5b7aea4df4e69c1b9d71fca97d43b416a3ad"
},
{
"ImportPath": "github.com/prometheus/procfs",
"Rev": "490cc6eb5fa45bf8a8b7b73c8bc82a8160e8531d"

View File

@ -0,0 +1,74 @@
// 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")
}
}

View File

@ -0,0 +1,126 @@
// 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)
}
}

View File

@ -0,0 +1,15 @@
// 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
[
{
"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
}
}
]
}
}
]

View File

@ -0,0 +1,318 @@
// 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)
}

View File

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

View File

@ -0,0 +1,84 @@
// 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()
}

View File

@ -0,0 +1,127 @@
// 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
}

View File

@ -0,0 +1,185 @@
// 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)
}
}

View File

@ -0,0 +1,106 @@
// 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
}

View File

@ -0,0 +1,225 @@
// 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
}

View File

@ -0,0 +1,40 @@
// 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
}

View File

@ -0,0 +1,100 @@
// 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)
}
}
}

View File

@ -22,25 +22,30 @@ import (
// For our purposes, FNV-1A 64-bit is used.
type Fingerprint uint64
// 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
}
// 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 0, err
}
return Fingerprint(num), nil
}
func (f Fingerprint) String() string {
return fmt.Sprintf("%016x", uint64(f))
}
// 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 {
num, err := strconv.ParseUint(s, 16, 64)
if err != nil {
return err
}
*f = Fingerprint(num)
return nil
}
// Fingerprints represents a collection of Fingerprint subject to a given
// natural sorting scheme. It implements sort.Interface.
type Fingerprints []Fingerprint

View File

@ -0,0 +1,71 @@
// 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, ", ")
}

View File

@ -0,0 +1,55 @@
// 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)
}
}

View File

@ -0,0 +1,64 @@
// 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
}
}

View File

@ -1,4 +1,4 @@
// Copyright 2014 The Prometheus Authors
// 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
@ -11,26 +11,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Build only when actually fuzzing
// +build gofuzz
package model
package expfmt
import (
"sort"
)
import "bytes"
// A LabelValue is an associated value for a LabelName.
type LabelValue string
// 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))
// LabelValues is a sortable LabelValue slice. It implements sort.Interface.
type LabelValues []LabelValue
if err != nil {
return 0
}
return 1
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]
}

View File

@ -18,42 +18,6 @@ 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

View File

@ -14,6 +14,7 @@
package model
import (
"encoding/json"
"fmt"
"sort"
"strings"
@ -23,27 +24,19 @@ 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 LabelSet
type Metric map[LabelName]LabelValue
// Equal compares the metrics.
// Equal compares the fingerprints of both metrics.
func (m Metric) Equal(o Metric) bool {
return LabelSet(m).Equal(LabelSet(o))
return m.Fingerprint().Equal(o.Fingerprint())
}
// Before compares the metrics' underlying label sets.
// Before compares the fingerprints of both metrics.
func (m Metric) Before(o Metric) bool {
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
return m.Fingerprint().Less(o.Fingerprint())
}
// String implements Stringer.
func (m Metric) String() string {
metricName, hasName := m[MetricNameLabel]
numLabels := len(m) - 1
@ -71,11 +64,69 @@ func (m Metric) String() string {
// Fingerprint returns a Metric's Fingerprint.
func (m Metric) Fingerprint() Fingerprint {
return LabelSet(m).Fingerprint()
return metricToFingerprint(m)
}
// 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()
// 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)
}

View File

@ -0,0 +1,121 @@
// 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)
}
}
}

View File

@ -11,6 +11,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Package model contains common data structures that are shared across
// Prometheus componenets and libraries.
// Package model contains core representation of Prometheus client primitives.
package model

View File

@ -0,0 +1,79 @@
// 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
}

View File

@ -0,0 +1,126 @@
// 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)
}
}
}

View File

@ -0,0 +1,37 @@
// 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)
}

View File

@ -17,7 +17,6 @@ import (
"bytes"
"hash"
"hash/fnv"
"sort"
"sync"
)
@ -47,71 +46,36 @@ func getHashAndBuf() *hashAndBuf {
}
func putHashAndBuf(hb *hashAndBuf) {
hb.h.Reset()
hb.b.Reset()
hashAndBufPool.Put(hb)
}
// 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.)
// LabelsToSignature returns a unique signature (i.e., fingerprint) for a given
// label set.
func LabelsToSignature(labels map[string]string) uint64 {
if len(labels) == 0 {
return emptyLabelSignature
}
labelNames := make([]string, 0, len(labels))
for labelName := range labels {
labelNames = append(labelNames, labelName)
}
sort.Strings(labelNames)
var result uint64
hb := getHashAndBuf()
defer putHashAndBuf(hb)
for _, labelName := range labelNames {
for labelName, labelValue := range labels {
hb.b.WriteString(labelName)
hb.b.WriteByte(SeparatorByte)
hb.b.WriteString(labels[labelName])
hb.b.WriteByte(SeparatorByte)
hb.b.WriteString(labelValue)
hb.h.Write(hb.b.Bytes())
result ^= hb.h.Sum64()
hb.h.Reset()
hb.b.Reset()
}
return hb.h.Sum64()
return result
}
// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as
// metricToFingerprint works exactly as LabelsToSignature but takes a Metric as
// parameter (rather than a label map) and returns a Fingerprint.
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 {
func metricToFingerprint(m Metric) Fingerprint {
if len(m) == 0 {
return Fingerprint(emptyLabelSignature)
}
@ -119,7 +83,7 @@ func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
hb := getHashAndBuf()
defer putHashAndBuf(hb)
for labelName, labelValue := range ls {
for labelName, labelValue := range m {
hb.b.WriteString(string(labelName))
hb.b.WriteByte(SeparatorByte)
hb.b.WriteString(string(labelValue))
@ -133,15 +97,13 @@ func labelSetToFastFingerprint(ls LabelSet) 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. The labels passed in
// will be sorted by this function.
func SignatureForLabels(m Metric, labels ...LabelName) uint64 {
// specified LabelNames into the signature calculation.
func SignatureForLabels(m Metric, labels LabelNames) uint64 {
if len(m) == 0 || len(labels) == 0 {
return emptyLabelSignature
}
sort.Sort(LabelNames(labels))
var result uint64
hb := getHashAndBuf()
defer putHashAndBuf(hb)
@ -149,11 +111,12 @@ func SignatureForLabels(m Metric, labels ...LabelName) 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 hb.h.Sum64()
return result
}
// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
@ -164,27 +127,24 @@ func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
return emptyLabelSignature
}
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)
var result uint64
hb := getHashAndBuf()
defer putHashAndBuf(hb)
for _, labelName := range labelNames {
for labelName, labelValue := range m {
if _, exclude := labels[labelName]; exclude {
continue
}
hb.b.WriteString(string(labelName))
hb.b.WriteByte(SeparatorByte)
hb.b.WriteString(string(m[labelName]))
hb.b.WriteByte(SeparatorByte)
hb.b.WriteString(string(labelValue))
hb.h.Write(hb.b.Bytes())
result ^= hb.h.Sum64()
hb.h.Reset()
hb.b.Reset()
}
return hb.h.Sum64()
if result == 0 {
return emptyLabelSignature
}
return result
}

View File

@ -30,7 +30,7 @@ func TestLabelsToSignature(t *testing.T) {
},
{
in: map[string]string{"name": "garland, briggs", "fear": "love is not enough"},
out: 5799056148416392346,
out: 12952432476264840823,
},
}
@ -45,45 +45,21 @@ func TestLabelsToSignature(t *testing.T) {
func TestMetricToFingerprint(t *testing.T) {
var scenarios = []struct {
in LabelSet
in Metric
out Fingerprint
}{
{
in: LabelSet{},
in: Metric{},
out: 14695981039346656037,
},
{
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"},
in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
out: 12952432476264840823,
},
}
for i, scenario := range scenarios {
actual := labelSetToFastFingerprint(scenario.in)
actual := metricToFingerprint(scenario.in)
if actual != scenario.out {
t.Errorf("%d. expected %d, got %d", i, scenario.out, actual)
@ -105,12 +81,12 @@ func TestSignatureForLabels(t *testing.T) {
{
in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
labels: LabelNames{"fear", "name"},
out: 5799056148416392346,
out: 12952432476264840823,
},
{
in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"},
labels: LabelNames{"fear", "name"},
out: 5799056148416392346,
out: 12952432476264840823,
},
{
in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
@ -125,7 +101,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)
@ -152,17 +128,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: 5799056148416392346,
out: 12952432476264840823,
},
{
in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
labels: map[LabelName]struct{}{},
out: 5799056148416392346,
out: 12952432476264840823,
},
{
in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
labels: nil,
out: 5799056148416392346,
out: 12952432476264840823,
},
}
@ -188,21 +164,21 @@ func BenchmarkLabelToSignatureScalar(b *testing.B) {
}
func BenchmarkLabelToSignatureSingle(b *testing.B) {
benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5146282821936882169)
benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5147259542624943964)
}
func BenchmarkLabelToSignatureDouble(b *testing.B) {
benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717)
benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528)
}
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"}, 13843036195897128121)
benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676)
}
func benchmarkMetricToFingerprint(b *testing.B, ls LabelSet, e Fingerprint) {
func benchmarkMetricToFingerprint(b *testing.B, m Metric, e Fingerprint) {
for i := 0; i < b.N; i++ {
if a := labelSetToFingerprint(ls); a != e {
b.Fatalf("expected signature of %d for %s, got %d", e, ls, a)
if a := metricToFingerprint(m); a != e {
b.Fatalf("expected signature of %d for %s, got %d", e, m, a)
}
}
}
@ -212,42 +188,18 @@ func BenchmarkMetricToFingerprintScalar(b *testing.B) {
}
func BenchmarkMetricToFingerprintSingle(b *testing.B) {
benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5146282821936882169)
benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value"}, 5147259542624943964)
}
func BenchmarkMetricToFingerprintDouble(b *testing.B) {
benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717)
benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528)
}
func BenchmarkMetricToFingerprintTriple(b *testing.B) {
benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121)
benchmarkMetricToFingerprint(b, Metric{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676)
}
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) {
func TestEmptyLabelSignature(t *testing.T) {
input := []map[string]string{nil, {}}
var ms runtime.MemStats
@ -262,11 +214,11 @@ func BenchmarkEmptyLabelSignature(b *testing.B) {
runtime.ReadMemStats(&ms)
if got := ms.Alloc; alloc != got {
b.Fatal("expected LabelsToSignature with empty labels not to perform allocations")
t.Fatal("expected LabelsToSignature with empty labels not to perform allocations")
}
}
func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerprint, concLevel int) {
func benchmarkMetricToFingerprintConc(b *testing.B, m Metric, e Fingerprint, concLevel int) {
var start, end sync.WaitGroup
start.Add(1)
end.Add(concLevel)
@ -275,8 +227,8 @@ func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerpri
go func() {
start.Wait()
for j := b.N / concLevel; j >= 0; j-- {
if a := labelSetToFastFingerprint(ls); a != e {
b.Fatalf("expected signature of %d for %s, got %d", e, ls, a)
if a := metricToFingerprint(m); a != e {
b.Fatalf("expected signature of %d for %s, got %d", e, m, a)
}
}
end.Done()
@ -287,18 +239,18 @@ func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerpri
end.Wait()
}
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 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 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 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 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 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 BenchmarkMetricToFastFingerprintTripleConc8(b *testing.B) {
benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8)
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)
}

View File

@ -0,0 +1,116 @@
// 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)
}

View File

@ -15,13 +15,13 @@ package model
import (
"testing"
"time"
native_time "time"
)
func TestComparators(t *testing.T) {
t1a := TimeFromUnix(0)
t1b := TimeFromUnix(0)
t2 := TimeFromUnix(2*second - 1)
t1a := TimestampFromUnix(0)
t1b := TimestampFromUnix(0)
t2 := TimestampFromUnix(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 TestTimeConversions(t *testing.T) {
func TestTimestampConversions(t *testing.T) {
unixSecs := int64(1136239445)
unixNsecs := int64(123456789)
unixNano := unixSecs*1e9 + unixNsecs
unixNano := unixSecs*1000000000 + unixNsecs
t1 := time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick)
t2 := time.Unix(unixSecs, unixNsecs)
t1 := native_time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick)
t2 := native_time.Unix(unixSecs, unixNsecs)
ts := TimeFromUnixNano(unixNano)
ts := TimestampFromUnixNano(unixNano)
if !ts.Time().Equal(t1) {
t.Fatalf("Expected %s, got %s", t1, ts.Time())
}
// Test available precision.
ts = TimeFromUnixNano(t2.UnixNano())
ts = TimestampFromTime(t2)
if !ts.Time().Equal(t1) {
t.Fatalf("Expected %s, got %s", t1, ts.Time())
}
@ -70,10 +70,10 @@ func TestTimeConversions(t *testing.T) {
}
func TestDuration(t *testing.T) {
duration := time.Second + time.Minute + time.Hour
goTime := time.Unix(1136239445, 0)
duration := native_time.Second + native_time.Minute + native_time.Hour
goTime := native_time.Unix(1136239445, 0)
ts := TimeFromUnix(goTime.Unix())
ts := TimestampFromTime(goTime)
if !goTime.Add(duration).Equal(ts.Add(duration).Time()) {
t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration))
}

View File

@ -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 metric. Do not use it for regular handling of a
// Prometheus metrics. 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)

View File

@ -9,20 +9,18 @@ import (
"sort"
"strings"
"github.com/golang/protobuf/proto"
"github.com/prometheus/client_golang/model"
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.:
@ -136,7 +134,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
for _, val := range labelValues {
b.Reset()
b.WriteString(val)
b.WriteByte(separatorByte)
b.WriteByte(model.SeparatorByte)
h.Write(b.Bytes())
}
d.id = h.Sum64()
@ -147,12 +145,12 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
h.Reset()
b.Reset()
b.WriteString(help)
b.WriteByte(separatorByte)
b.WriteByte(model.SeparatorByte)
h.Write(b.Bytes())
for _, labelName := range labelNames {
b.Reset()
b.WriteString(labelName)
b.WriteByte(separatorByte)
b.WriteByte(model.SeparatorByte)
h.Write(b.Bytes())
}
d.dimHash = h.Sum64()
@ -197,5 +195,5 @@ func (d *Desc) String() string {
func checkLabelName(l string) bool {
return labelNameRE.MatchString(l) &&
!strings.HasPrefix(l, reservedLabelPrefix)
!strings.HasPrefix(l, model.ReservedLabelPrefix)
}

View File

@ -61,13 +61,12 @@
// It also exports some stats about the HTTP usage of the /metrics
// endpoint. (See the Handler function for more detail.)
//
// Two more advanced metric types are the Summary and Histogram.
// A more advanced metric type is the Summary.
//
// 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.
// 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.
//
// Those are all the parts needed for basic usage. Detailed documentation and
// examples are provided below.

View File

@ -392,9 +392,6 @@ 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
@ -417,26 +414,6 @@ 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: <
@ -478,56 +455,6 @@ 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",
@ -574,64 +501,6 @@ 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{

View File

@ -2,13 +2,10 @@ package prometheus
import (
"runtime"
"runtime/debug"
"time"
)
type goCollector struct {
goroutines Gauge
gcDesc *Desc
}
// NewGoCollector returns a collector which exports metrics about the current
@ -16,35 +13,19 @@ type goCollector struct {
func NewGoCollector() *goCollector {
return &goCollector{
goroutines: NewGauge(GaugeOpts{
Name: "go_goroutines",
Name: "process_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)
}

View File

@ -1,7 +1,7 @@
package prometheus
import (
"runtime"
"reflect"
"testing"
"time"
@ -35,9 +35,6 @@ 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())
@ -50,71 +47,9 @@ 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")

View File

@ -22,6 +22,7 @@ import (
"github.com/golang/protobuf/proto"
"github.com/prometheus/client_golang/model"
dto "github.com/prometheus/client_model/go"
)
@ -48,10 +49,6 @@ 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
@ -60,7 +57,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", bucketLabel,
"%q is not allowed as label name in histograms", model.BucketLabel,
)
)
@ -150,7 +147,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 DefBuckets.
// implicitly. The default value is DefObjectives.
Buckets []float64
}
@ -174,12 +171,12 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
}
for _, n := range desc.variableLabels {
if n == bucketLabel {
if n == model.BucketLabel {
panic(errBucketLabelNotAllowed)
}
}
for _, lp := range desc.constLabelPairs {
if lp.GetName() == bucketLabel {
if lp.GetName() == model.BucketLabel {
panic(errBucketLabelNotAllowed)
}
}
@ -216,13 +213,6 @@ 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.
@ -232,6 +222,9 @@ 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 {
@ -349,102 +342,3 @@ 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()
}

View File

@ -124,10 +124,6 @@ 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 {
@ -202,10 +198,6 @@ 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))

View File

@ -14,9 +14,6 @@
package prometheus
import (
"bufio"
"io"
"net"
"net/http"
"strconv"
"strings"
@ -144,18 +141,7 @@ func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.Respo
urlLen = len(r.URL.String())
}
go computeApproximateRequestSize(r, out, urlLen)
_, 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)
handlerFunc(delegate, r)
elapsed := float64(time.Since(now)) / float64(time.Microsecond)
@ -192,7 +178,7 @@ type responseWriterDelegator struct {
handler, method string
status int
written int64
written int
wroteHeader bool
}
@ -207,32 +193,7 @@ func (r *responseWriterDelegator) Write(b []byte) (int, error) {
r.WriteHeader(http.StatusOK)
}
n, err := r.ResponseWriter.Write(b)
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
r.written += n
return n, err
}

View File

@ -19,8 +19,6 @@ 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

View File

@ -33,10 +33,13 @@ 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 (
@ -167,11 +170,6 @@ 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
@ -343,7 +341,7 @@ func (r *registry) Push(job, instance, pushURL, method string) error {
}
buf := r.getBuf()
defer r.giveBuf(buf)
if err := r.writePB(expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)); err != nil {
if _, err := r.writePB(buf, text.WriteProtoDelimited); err != nil {
if r.panicOnCollectError {
panic(err)
}
@ -366,11 +364,11 @@ func (r *registry) Push(job, instance, pushURL, method string) error {
}
func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
contentType := expfmt.Negotiate(req.Header)
enc, contentType := chooseEncoder(req)
buf := r.getBuf()
defer r.giveBuf(buf)
writer, encoding := decorateWriter(req, buf)
if err := r.writePB(expfmt.NewEncoder(writer, contentType)); err != nil {
if _, err := r.writePB(writer, enc); err != nil {
if r.panicOnCollectError {
panic(err)
}
@ -381,7 +379,7 @@ func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
closer.Close()
}
header := w.Header()
header.Set(contentTypeHeader, string(contentType))
header.Set(contentTypeHeader, contentType)
header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
if encoding != "" {
header.Set(contentEncodingHeader, encoding)
@ -389,7 +387,7 @@ func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Write(buf.Bytes())
}
func (r *registry) writePB(encoder expfmt.Encoder) error {
func (r *registry) writePB(w io.Writer, writeEncoded encoder) (int, error) {
var metricHashes map[uint64]struct{}
if r.collectChecksEnabled {
metricHashes = make(map[uint64]struct{})
@ -441,7 +439,7 @@ func (r *registry) writePB(encoder expfmt.Encoder) 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 fmt.Errorf("error collecting metric %v: %s", desc, err)
return 0, fmt.Errorf("error collecting metric %v: %s", desc, err)
}
switch {
case metricFamily.Type != nil:
@ -457,11 +455,11 @@ func (r *registry) writePB(encoder expfmt.Encoder) error {
case dtoMetric.Histogram != nil:
metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
default:
return fmt.Errorf("empty metric collected: %s", dtoMetric)
return 0, fmt.Errorf("empty metric collected: %s", dtoMetric)
}
if r.collectChecksEnabled {
if err := r.checkConsistency(metricFamily, dtoMetric, desc, metricHashes); err != nil {
return err
return 0, err
}
}
metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
@ -475,7 +473,7 @@ func (r *registry) writePB(encoder expfmt.Encoder) error {
if r.collectChecksEnabled {
for _, m := range mf.Metric {
if err := r.checkConsistency(mf, m, nil, metricHashes); err != nil {
return err
return 0, err
}
}
}
@ -484,7 +482,7 @@ func (r *registry) writePB(encoder expfmt.Encoder) error {
for _, m := range mf.Metric {
if r.collectChecksEnabled {
if err := r.checkConsistency(existingMF, m, nil, metricHashes); err != nil {
return err
return 0, err
}
}
existingMF.Metric = append(existingMF.Metric, m)
@ -505,12 +503,15 @@ func (r *registry) writePB(encoder expfmt.Encoder) error {
}
sort.Strings(names)
var written int
for _, name := range names {
if err := encoder.Encode(metricFamiliesByName[name]); err != nil {
return err
w, err := writeEncoded(w, metricFamiliesByName[name])
written += w
if err != nil {
return written, err
}
}
return nil
return written, nil
}
func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *dto.Metric, desc *Desc, metricHashes map[uint64]struct{}) error {
@ -519,11 +520,10 @@ 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 %s %s is not a %s",
metricFamily.GetName(), dtoMetric, metricFamily.GetType(),
"collected metric %q is not a %s",
dtoMetric, metricFamily.Type,
)
}
@ -531,24 +531,19 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d
h := fnv.New64a()
var buf bytes.Buffer
buf.WriteString(metricFamily.GetName())
buf.WriteByte(separatorByte)
buf.WriteByte(model.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(separatorByte)
buf.WriteByte(model.SeparatorByte)
h.Write(buf.Bytes())
}
metricHash := h.Sum64()
if _, exists := metricHashes[metricHash]; exists {
return fmt.Errorf(
"collected metric %s %s was collected before with the same name and label values",
metricFamily.GetName(), dtoMetric,
"collected metric %q was collected before with the same name and label values",
dtoMetric,
)
}
metricHashes[metricHash] = struct{}{}
@ -560,14 +555,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 %s %s has name %q but should have %q",
metricFamily.GetName(), dtoMetric, metricFamily.GetName(), desc.fqName,
"collected metric %q has name %q but should have %q",
dtoMetric, metricFamily.GetName(), desc.fqName,
)
}
if metricFamily.GetHelp() != desc.help {
return fmt.Errorf(
"collected metric %s %s has help %q but should have %q",
metricFamily.GetName(), dtoMetric, metricFamily.GetHelp(), desc.help,
"collected metric %q has help %q but should have %q",
dtoMetric, metricFamily.GetHelp(), desc.help,
)
}
@ -581,8 +576,8 @@ func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *d
}
if len(lpsFromDesc) != len(dtoMetric.Label) {
return fmt.Errorf(
"labels in collected metric %s %s are inconsistent with descriptor %s",
metricFamily.GetName(), dtoMetric, desc,
"labels in collected metric %q are inconsistent with descriptor %s",
dtoMetric, desc,
)
}
sort.Sort(LabelPairSorter(lpsFromDesc))
@ -591,8 +586,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 %s %s are inconsistent with descriptor %s",
metricFamily.GetName(), dtoMetric, desc,
"labels in collected metric %q are inconsistent with descriptor %s",
dtoMetric, desc,
)
}
}
@ -602,10 +597,7 @@ 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 %s %s with unregistered descriptor %s",
metricFamily.GetName(), dtoMetric, desc,
)
return fmt.Errorf("collected metric %q with unregistered descriptor %s", dtoMetric, desc)
}
return nil
@ -680,6 +672,34 @@ 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).

View File

@ -68,14 +68,14 @@ func testHandler(t testing.TB) {
Metric: []*dto.Metric{
{
Label: []*dto.LabelPair{
{
Name: proto.String("externalconstname"),
Value: proto.String("externalconstvalue"),
},
{
Name: proto.String("externallabelname"),
Value: proto.String("externalval1"),
},
{
Name: proto.String("externalconstname"),
Value: proto.String("externalconstvalue"),
},
},
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{externalconstname="externalconstvalue",externallabelname="externalval1"} 1
externalname{externallabelname="externalval1",externalconstname="externalconstvalue"} 1
`)
externalMetricFamilyAsProtoText := []byte(`name: "externalname"
help: "externaldocstring"
type: COUNTER
metric: <
label: <
name: "externalconstname"
value: "externalconstvalue"
>
label: <
name: "externallabelname"
value: "externalval1"
>
label: <
name: "externalconstname"
value: "externalconstvalue"
>
counter: <
value: 1
>
>
`)
externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric:<label:<name:"externalconstname" value:"externalconstvalue" > label:<name:"externallabelname" value:"externalval1" > counter:<value:1 > >
externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric:<label:<name:"externallabelname" value:"externalval1" > label:<name:"externalconstname" value:"externalconstvalue" > counter:<value:1 > >
`)
expectedMetricFamily := &dto.MetricFamily{

View File

@ -25,11 +25,9 @@ import (
"github.com/golang/protobuf/proto"
dto "github.com/prometheus/client_model/go"
)
// quantileLabel is used for the label that defines the quantile in a
// summary.
const quantileLabel = "quantile"
"github.com/prometheus/client_golang/model"
)
// A Summary captures individual observations from an event or sample stream and
// summarizes them in a manner similar to traditional summary statistics: 1. sum
@ -59,7 +57,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", quantileLabel,
"%q is not allowed as label name in summaries", model.QuantileLabel,
)
)
@ -114,9 +112,7 @@ type SummaryOpts struct {
ConstLabels Labels
// Objectives defines the quantile rank estimates with their respective
// 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.
// absolute error. The default value is DefObjectives.
Objectives map[float64]float64
// MaxAge defines the duration for which an observation stays relevant
@ -174,12 +170,12 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
}
for _, n := range desc.variableLabels {
if n == quantileLabel {
if n == model.QuantileLabel {
panic(errQuantileLabelNotAllowed)
}
}
for _, lp := range desc.constLabelPairs {
if lp.GetName() == quantileLabel {
if lp.GetName() == model.QuantileLabel {
panic(errQuantileLabelNotAllowed)
}
}
@ -452,89 +448,3 @@ 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
}

View File

@ -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 Untyped instances, use NewUntyped.
// To create Gauge instances, use NewUntyped.
type Untyped interface {
Metric
Collector

View File

@ -43,15 +43,11 @@ 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
}

View File

@ -58,11 +58,6 @@ 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
@ -92,9 +87,8 @@ 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
// creating a Metric without using it and keeping the Metric for later use are
// the same as for GetMetricWithLabelValues.
// accessed for the first time, a new Metric is created. Implications of keeping
// the Metric 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.

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package expfmt
package text
import (
"bytes"
@ -19,14 +19,11 @@ 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.

View File

@ -11,7 +11,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package expfmt
// 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
import (
"bytes"
@ -20,8 +27,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
@ -72,7 +79,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 %s", name, metric,
"expected counter in metric %s", metric,
)
}
n, err = writeSample(
@ -83,7 +90,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 %s", name, metric,
"expected gauge in metric %s", metric,
)
}
n, err = writeSample(
@ -94,7 +101,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 %s", name, metric,
"expected untyped in metric %s", metric,
)
}
n, err = writeSample(
@ -105,7 +112,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 %s", name, metric,
"expected summary in metric %s", metric,
)
}
for _, q := range metric.Summary.Quantile {
@ -137,7 +144,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
case dto.MetricType_HISTOGRAM:
if metric.Histogram == nil {
return written, fmt.Errorf(
"expected histogram in metric %s %s", name, metric,
"expected summary in metric %s", metric,
)
}
infSeen := false
@ -184,7 +191,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
)
default:
return written, fmt.Errorf(
"unexpected type in metric %s %s", name, metric,
"unexpected type in metric %s", metric,
)
}
written += n

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package expfmt
package text
import (
"bytes"
@ -20,7 +20,6 @@ import (
"testing"
"github.com/golang/protobuf/proto"
dto "github.com/prometheus/client_model/go"
)

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package expfmt
package text
import (
"bufio"
@ -25,7 +25,7 @@ import (
dto "github.com/prometheus/client_model/go"
"github.com/golang/protobuf/proto"
"github.com/prometheus/common/model"
"github.com/prometheus/client_golang/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)
}
// TextParser is used to parse the simple and flat text-based exchange format. Its
// Parser is used to parse the simple and flat text-based exchange format. Its
// nil value is ready to use.
type TextParser struct {
type Parser struct {
metricFamiliesByName map[string]*dto.MetricFamily
buf *bufio.Reader // Where the parsed input is read through.
err error // Most recent error.
@ -83,21 +83,14 @@ type TextParser 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 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.
// 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.
//
// This method must not be called concurrently. If you want to parse different
// input concurrently, instantiate a separate Parser for each goroutine.
func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
func (p *Parser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
p.reset(in)
for nextState := p.startOfLine; nextState != nil; nextState = nextState() {
// Magic happens here...
@ -111,7 +104,7 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF
return p.metricFamiliesByName, p.err
}
func (p *TextParser) reset(in io.Reader) {
func (p *Parser) reset(in io.Reader) {
p.metricFamiliesByName = map[string]*dto.MetricFamily{}
if p.buf == nil {
p.buf = bufio.NewReader(in)
@ -132,7 +125,7 @@ func (p *TextParser) 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 *TextParser) startOfLine() stateFn {
func (p *Parser) startOfLine() stateFn {
p.lineCount++
if p.skipBlankTab(); p.err != nil {
// End of input reached. This is the only case where
@ -151,7 +144,7 @@ func (p *TextParser) 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 *TextParser) startComment() stateFn {
func (p *Parser) startComment() stateFn {
if p.skipBlankTab(); p.err != nil {
return nil // Unexpected end of input.
}
@ -212,7 +205,7 @@ func (p *TextParser) 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 *TextParser) readingMetricName() stateFn {
func (p *Parser) readingMetricName() stateFn {
if p.readTokenAsMetricName(); p.err != nil {
return nil
}
@ -239,7 +232,7 @@ func (p *TextParser) 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 *TextParser) readingLabels() stateFn {
func (p *Parser) readingLabels() stateFn {
// Summaries/histograms are special. We have to reset the
// currentLabels map, currentQuantile and currentBucket before starting to
// read labels.
@ -257,7 +250,7 @@ func (p *TextParser) 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 *TextParser) startLabelName() stateFn {
func (p *Parser) startLabelName() stateFn {
if p.skipBlankTab(); p.err != nil {
return nil // Unexpected end of input.
}
@ -297,7 +290,7 @@ func (p *TextParser) 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 *TextParser) startLabelValue() stateFn {
func (p *Parser) startLabelValue() stateFn {
if p.skipBlankTab(); p.err != nil {
return nil // Unexpected end of input.
}
@ -355,7 +348,7 @@ func (p *TextParser) 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 *TextParser) readingValue() stateFn {
func (p *Parser) 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.
@ -443,7 +436,7 @@ func (p *TextParser) 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 *TextParser) startTimestamp() stateFn {
func (p *Parser) startTimestamp() stateFn {
if p.skipBlankTab(); p.err != nil {
return nil // Unexpected end of input.
}
@ -469,7 +462,7 @@ func (p *TextParser) 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 *TextParser) readingHelp() stateFn {
func (p *Parser) readingHelp() stateFn {
if p.currentMF.Help != nil {
p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName()))
return nil
@ -484,7 +477,7 @@ func (p *TextParser) 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 *TextParser) readingType() stateFn {
func (p *Parser) 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
@ -504,7 +497,7 @@ func (p *TextParser) readingType() stateFn {
// parseError sets p.err to a ParseError at the current line with the given
// message.
func (p *TextParser) parseError(msg string) {
func (p *Parser) parseError(msg string) {
p.err = ParseError{
Line: p.lineCount,
Msg: msg,
@ -513,7 +506,7 @@ func (p *TextParser) 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 *TextParser) skipBlankTab() {
func (p *Parser) skipBlankTab() {
for {
if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) {
return
@ -523,7 +516,7 @@ func (p *TextParser) skipBlankTab() {
// skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do
// anything if p.currentByte is neither ' ' nor '\t'.
func (p *TextParser) skipBlankTabIfCurrentBlankTab() {
func (p *Parser) skipBlankTabIfCurrentBlankTab() {
if isBlankOrTab(p.currentByte) {
p.skipBlankTab()
}
@ -533,7 +526,7 @@ func (p *TextParser) 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 *TextParser) readTokenUntilWhitespace() {
func (p *Parser) readTokenUntilWhitespace() {
p.currentToken.Reset()
for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' {
p.currentToken.WriteByte(p.currentByte)
@ -547,7 +540,7 @@ func (p *TextParser) 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 *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
func (p *Parser) readTokenUntilNewline(recognizeEscapeSequence bool) {
p.currentToken.Reset()
escaped := false
for p.err == nil {
@ -580,7 +573,7 @@ func (p *TextParser) 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 *TextParser) readTokenAsMetricName() {
func (p *Parser) readTokenAsMetricName() {
p.currentToken.Reset()
if !isValidMetricNameStart(p.currentByte) {
return
@ -598,7 +591,7 @@ func (p *TextParser) 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 *TextParser) readTokenAsLabelName() {
func (p *Parser) readTokenAsLabelName() {
p.currentToken.Reset()
if !isValidLabelNameStart(p.currentByte) {
return
@ -617,7 +610,7 @@ func (p *TextParser) 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 *TextParser) readTokenAsLabelValue() {
func (p *Parser) readTokenAsLabelValue() {
p.currentToken.Reset()
escaped := false
for {
@ -651,7 +644,7 @@ func (p *TextParser) readTokenAsLabelValue() {
}
}
func (p *TextParser) setOrCreateCurrentMF() {
func (p *Parser) setOrCreateCurrentMF() {
p.currentIsSummaryCount = false
p.currentIsSummarySum = false
p.currentIsHistogramCount = false

View File

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package expfmt
package text
import (
"math"
@ -22,7 +22,9 @@ import (
dto "github.com/prometheus/client_model/go"
)
func testTextParse(t testing.TB) {
var parser Parser
func testParse(t testing.TB) {
var scenarios = []struct {
in string
out []*dto.MetricFamily
@ -417,17 +419,17 @@ request_duration_microseconds_count 2693
}
}
func TestTextParse(t *testing.T) {
testTextParse(t)
func TestParse(t *testing.T) {
testParse(t)
}
func BenchmarkTextParse(b *testing.B) {
func BenchmarkParse(b *testing.B) {
for i := 0; i < b.N; i++ {
testTextParse(b)
testParse(b)
}
}
func testTextParseError(t testing.TB) {
func testParseError(t testing.TB) {
var scenarios = []struct {
in string
err string
@ -575,12 +577,12 @@ metric_bucket{le="bla"} 3.14
}
func TestTextParseError(t *testing.T) {
testTextParseError(t)
func TestParseError(t *testing.T) {
testParseError(t)
}
func BenchmarkParseError(b *testing.B) {
for i := 0; i < b.N; i++ {
testTextParseError(b)
testParseError(b)
}
}

View File

@ -0,0 +1,43 @@
// 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)
}

Binary file not shown.

View File

@ -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 go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 70
# HELP process_goroutines Number of goroutines that currently exist.
# TYPE process_goroutines gauge
process_goroutines 70
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 8192

Binary file not shown.

View File

@ -1,388 +0,0 @@
// 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
}
// NewDecoder returns a new decoder based on the HTTP header.
func NewDecoder(r io.Reader, h http.Header) (Decoder, error) {
ct := h.Get(hdrContentType)
mediatype, params, err := mime.ParseMediaType(ct)
if err != nil {
return nil, fmt.Errorf("invalid Content-Type header %q: %s", ct, err)
}
const (
protoType = ProtoType + "/" + ProtoSubType
textType = "text/plain"
)
switch mediatype {
case protoType:
if p := params["proto"]; p != ProtoProtocol {
return nil, fmt.Errorf("unrecognized protocol message %s", p)
}
if e := params["encoding"]; e != "delimited" {
return nil, fmt.Errorf("unsupported encoding %s", e)
}
return &protoDecoder{r: r}, nil
case textType:
if v, ok := params["version"]; ok && v != "0.0.4" {
return nil, fmt.Errorf("unrecognized protocol version %s", v)
}
return &textDecoder{r: r}, nil
default:
return nil, fmt.Errorf("unsupported media type %q, expected %q or %q", mediatype, protoType, textType)
}
}
// 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,
})
}
if m.Summary.SampleSum != nil {
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,
})
}
if m.Summary.SampleCount != nil {
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,
})
}
if m.Histogram.SampleSum != nil {
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,
})
}
if m.Histogram.SampleCount != nil {
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 a 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
}

View File

@ -1,341 +0,0 @@
// 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",
"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.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 _, 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("output does not match")
}
}
}
func testDiscriminatorHTTPHeader(t testing.TB) {
var scenarios = []struct {
input map[string]string
output Decoder
err error
}{
{
input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`},
output: &protoDecoder{},
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: &textDecoder{},
err: nil,
},
{
input: map[string]string{"Content-Type": `text/plain`},
output: &textDecoder{},
err: nil,
},
{
input: map[string]string{"Content-Type": `text/plain; version=0.0.3`},
output: nil,
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 := NewDecoder(nil, 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)
}
}

View File

@ -1,88 +0,0 @@
// 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 == ProtoType && ac.SubType == ProtoSubType && 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")
}

View File

@ -1,37 +0,0 @@
// 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`
ProtoSubType = `vnd.google.protobuf`
ProtoProtocol = `io.prometheus.client.MetricFamily`
ProtoFmt = ProtoType + "/" + ProtoSubType + "; 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`
)
const (
hdrContentType = "Content-Type"
hdrAccept = "Accept"
)

View File

@ -1,6 +0,0 @@
minimal_metric 1.234
another_metric -3e3 103948
# Even that:
no_labels{} 3
# HELP line for non-existing metric will be ignored.

View File

@ -1,12 +0,0 @@
# 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

View File

@ -1,22 +0,0 @@
# 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

View File

@ -1,10 +0,0 @@
# 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

View File

@ -1 +0,0 @@
metric{label="\t"} 3.14

View File

@ -1 +0,0 @@
metric{label="bla"} 3.14 2 3

View File

@ -1 +0,0 @@
metric{label="bla"} blubb

View File

@ -1,3 +0,0 @@
# HELP metric one
# HELP metric two

View File

@ -1,3 +0,0 @@
# TYPE metric counter
# TYPE metric untyped

View File

@ -1,3 +0,0 @@
metric 4.12
# TYPE metric counter

View File

@ -1,2 +0,0 @@
# TYPE metric bla

View File

@ -1,2 +0,0 @@
# TYPE met-ric

View File

@ -1 +0,0 @@
@invalidmetric{label="bla"} 3.14 2

View File

@ -1 +0,0 @@
{label="bla"} 3.14 2

View File

@ -1,3 +0,0 @@
# TYPE metric histogram
metric_bucket{le="bla"} 3.14

View File

@ -1,3 +0,0 @@
metric{label="new
line"} 3.14

View File

@ -1 +0,0 @@
metric{@="bla"} 3.14

View File

@ -1 +0,0 @@
metric{__name__="bla"} 3.14

View File

@ -1 +0,0 @@
metric{label+="bla"} 3.14

View File

@ -1 +0,0 @@
metric{label=bla} 3.14

View File

@ -1,3 +0,0 @@
# TYPE metric summary
metric{quantile="bla"} 3.14

View File

@ -1 +0,0 @@
metric{label="bla"+} 3.14

View File

@ -1 +0,0 @@
metric{label="bla"} 3.14 2.72

View File

@ -1 +0,0 @@
m{} 0

View File

@ -1,516 +0,0 @@
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

View File

@ -1,129 +0,0 @@
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

View File

@ -1,163 +0,0 @@
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

View File

@ -1,188 +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 (
"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]
}

View File

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

View File

@ -1,83 +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 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)
}
}

View File

@ -1,230 +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"
"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
}

View File

@ -1,395 +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 (
"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 "<ValNone>":
*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 "<ValNone>"
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")
}

View File

@ -1,362 +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 (
"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)
}
}
}

View File

@ -29,7 +29,6 @@ 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,6 +38,9 @@ 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.
@ -61,13 +63,10 @@ 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 }
// 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
}
// KubeletMetricIngester implements extraction.Ingester
type kubeletMetricIngester []KubeletMetric
func (k *kubeletMetricIngester) Ingest(samples model.Samples) error {
acceptedMethods := sets.NewString(
metrics.PodWorkerLatencyKey,
metrics.PodWorkerStartLatencyKey,
@ -79,7 +78,6 @@ func ParseKubeletMetrics(metricsBlob string) ([]KubeletMetric, error) {
metrics.DockerErrorsKey,
)
var kms []KubeletMetric
for _, sample := range samples {
const prefix = metrics.KubeletSubsystem + "_"
metricName := string(sample.Metric[model.MetricNameLabel])
@ -107,14 +105,16 @@ func ParseKubeletMetrics(metricsBlob string) ([]KubeletMetric, error) {
}
}
kms = append(kms, KubeletMetric{
operation,
method,
quantile,
time.Duration(int64(latency)) * time.Microsecond,
})
*k = append(*k, KubeletMetric{operation, method, quantile, time.Duration(int64(latency)) * time.Microsecond})
}
return kms, nil
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
}
// HighLatencyKubeletOperations logs and counts the high latency metrics exported by the kubelet server via /metrics.

View File

@ -50,8 +50,8 @@ import (
"k8s.io/kubernetes/pkg/watch"
"github.com/davecgh/go-spew/spew"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"github.com/prometheus/client_golang/extraction"
"github.com/prometheus/client_golang/model"
"golang.org/x/crypto/ssh"
. "github.com/onsi/ginkgo"
@ -1817,6 +1817,34 @@ type LatencyMetric struct {
Latency time.Duration
}
// latencyMetricIngestor implements extraction.Ingester
type latencyMetricIngester []LatencyMetric
func (l *latencyMetricIngester) Ingest(samples model.Samples) error {
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"])
latency := sample.Value
quantile, err := strconv.ParseFloat(string(sample.Metric[model.QuantileLabel]), 64)
if err != nil {
return err
}
*l = append(*l, LatencyMetric{
verb,
resource,
quantile,
time.Duration(int64(latency)) * time.Microsecond,
})
}
return nil
}
// LatencyMetricByLatency implements sort.Interface for []LatencyMetric based on
// the latency field.
type LatencyMetricByLatency []LatencyMetric
@ -1830,37 +1858,9 @@ func ReadLatencyMetrics(c *client.Client) ([]LatencyMetric, error) {
if err != nil {
return nil, err
}
samples, err := extractMetricSamples(body)
if err != nil {
return nil, err
}
var metrics []LatencyMetric
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"])
latency := sample.Value
quantile, err := strconv.ParseFloat(string(sample.Metric[model.QuantileLabel]), 64)
if err != nil {
return metrics, err
}
metrics = append(metrics, LatencyMetric{
verb,
resource,
quantile,
time.Duration(int64(latency)) * time.Microsecond,
})
}
return metrics, nil
var ingester latencyMetricIngester
err = extraction.Processor004.ProcessSingle(strings.NewReader(body), &ingester, &extraction.ProcessOptions{})
return ingester, err
}
// Prints summary metrics for request types with latency above threshold
@ -2070,29 +2070,3 @@ func waitForClusterSize(c *client.Client, size int, timeout time.Duration) error
}
return fmt.Errorf("timeout waiting %v for cluster size to be %d", timeout, size)
}
// extractMetricSamples parses the prometheus metric samples from the input string.
func extractMetricSamples(metricsBlob string) ([]*model.Sample, error) {
dec, err := expfmt.NewDecoder(strings.NewReader(metricsBlob),
http.Header{"Content-Type": []string{"text/plain"}})
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...)
}
}