429 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			429 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | // Copyright 2021 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 textparse | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"encoding/binary" | ||
|  | 	"io" | ||
|  | 	"testing" | ||
|  | 
 | ||
|  | 	"github.com/gogo/protobuf/proto" | ||
|  | 	"github.com/stretchr/testify/require" | ||
|  | 
 | ||
|  | 	"github.com/prometheus/prometheus/pkg/exemplar" | ||
|  | 	"github.com/prometheus/prometheus/pkg/histogram" | ||
|  | 	"github.com/prometheus/prometheus/pkg/labels" | ||
|  | 
 | ||
|  | 	dto "github.com/prometheus/prometheus/prompb/io/prometheus/client" | ||
|  | ) | ||
|  | 
 | ||
|  | func TestProtobufParse(t *testing.T) { | ||
|  | 	textMetricFamilies := []string{ | ||
|  | 		`name: "go_build_info" | ||
|  | help: "Build information about the main Go module." | ||
|  | type: GAUGE | ||
|  | metric: < | ||
|  |   label: < | ||
|  |     name: "checksum" | ||
|  |     value: "" | ||
|  |   > | ||
|  |   label: < | ||
|  |     name: "path" | ||
|  |     value: "github.com/prometheus/client_golang" | ||
|  |   > | ||
|  |   label: < | ||
|  |     name: "version" | ||
|  |     value: "(devel)" | ||
|  |   > | ||
|  |   gauge: < | ||
|  |     value: 1 | ||
|  |   > | ||
|  | > | ||
|  | 
 | ||
|  | `, | ||
|  | 		`name: "go_memstats_alloc_bytes_total" | ||
|  | help: "Total number of bytes allocated, even if freed." | ||
|  | type: COUNTER | ||
|  | metric: < | ||
|  |   counter: < | ||
|  |     value: 1.546544e+06 | ||
|  |     exemplar: < | ||
|  |       label: < | ||
|  |         name: "dummyID" | ||
|  |         value: "42" | ||
|  |       > | ||
|  |       value: 12 | ||
|  |       timestamp: < | ||
|  |         seconds: 1625851151 | ||
|  |         nanos: 233181499 | ||
|  |       > | ||
|  |     > | ||
|  |   > | ||
|  | > | ||
|  | 
 | ||
|  | `, | ||
|  | 		`name: "something_untyped" | ||
|  | help: "Just to test the untyped type." | ||
|  | type: UNTYPED | ||
|  | metric: < | ||
|  |   untyped: < | ||
|  |     value: 42 | ||
|  |   > | ||
|  |   timestamp_ms: 1234567 | ||
|  | > | ||
|  | 
 | ||
|  | `, | ||
|  | 		`name: "test_histogram" | ||
|  | help: "Test histogram with many buckets removed to keep it manageable in size." | ||
|  | type: HISTOGRAM | ||
|  | metric: < | ||
|  |   histogram: < | ||
|  |     sample_count: 175 | ||
|  |     sample_sum: 0.0008280461746287094 | ||
|  |     bucket: < | ||
|  |       cumulative_count: 2 | ||
|  |       upper_bound: -0.0004899999999999998 | ||
|  |     > | ||
|  |     bucket: < | ||
|  |       cumulative_count: 4 | ||
|  |       upper_bound: -0.0003899999999999998 | ||
|  |       exemplar: < | ||
|  |         label: < | ||
|  |           name: "dummyID" | ||
|  |           value: "59727" | ||
|  |         > | ||
|  |         value: -0.0003919818421972943 | ||
|  |         timestamp: < | ||
|  |           seconds: 1625851155 | ||
|  |           nanos: 146848499 | ||
|  |         > | ||
|  |       > | ||
|  |     > | ||
|  |     bucket: < | ||
|  |       cumulative_count: 16 | ||
|  |       upper_bound: -0.0002899999999999998 | ||
|  |       exemplar: < | ||
|  |         label: < | ||
|  |           name: "dummyID" | ||
|  |           value: "5617" | ||
|  |         > | ||
|  |         value: -0.0002956962622126468 | ||
|  |         timestamp: < | ||
|  |           seconds: 1625851150 | ||
|  |           nanos: 233181498 | ||
|  |         > | ||
|  |       > | ||
|  |     > | ||
|  |     sb_schema: 3 | ||
|  |     sb_zero_threshold: 2.938735877055719e-39 | ||
|  |     sb_zero_count: 2 | ||
|  |     sb_negative: < | ||
|  |       span: < | ||
|  |         offset: -162 | ||
|  |         length: 1 | ||
|  |       > | ||
|  |       span: < | ||
|  |         offset: 23 | ||
|  |         length: 4 | ||
|  |       > | ||
|  |       delta: 1 | ||
|  |       delta: 3 | ||
|  |       delta: -2 | ||
|  |       delta: -1 | ||
|  |       delta: 1 | ||
|  |     > | ||
|  |     sb_positive: < | ||
|  |       span: < | ||
|  |         offset: -161 | ||
|  |         length: 1 | ||
|  |       > | ||
|  |       span: < | ||
|  |         offset: 8 | ||
|  |         length: 3 | ||
|  |       > | ||
|  |       delta: 1 | ||
|  |       delta: 2 | ||
|  |       delta: -1 | ||
|  |       delta: -1 | ||
|  |     > | ||
|  |   > | ||
|  |   timestamp_ms: 1234568 | ||
|  | > | ||
|  | 
 | ||
|  | `, | ||
|  | 		`name: "test_histogram2" | ||
|  | help: "Same histogram as before but now without sparse buckets." | ||
|  | type: HISTOGRAM | ||
|  | metric: < | ||
|  |   histogram: < | ||
|  |     sample_count: 175 | ||
|  |     sample_sum: 0.0008280461746287094 | ||
|  |     bucket: < | ||
|  |       cumulative_count: 2 | ||
|  |       upper_bound: -0.0004899999999999998 | ||
|  |     > | ||
|  |     bucket: < | ||
|  |       cumulative_count: 4 | ||
|  |       upper_bound: -0.0003899999999999998 | ||
|  |       exemplar: < | ||
|  |         label: < | ||
|  |           name: "dummyID" | ||
|  |           value: "59727" | ||
|  |         > | ||
|  |         value: -0.0003919818421972943 | ||
|  |         timestamp: < | ||
|  |           seconds: 1625851155 | ||
|  |           nanos: 146848499 | ||
|  |         > | ||
|  |       > | ||
|  |     > | ||
|  |     bucket: < | ||
|  |       cumulative_count: 16 | ||
|  |       upper_bound: -0.0002899999999999998 | ||
|  |       exemplar: < | ||
|  |         label: < | ||
|  |           name: "dummyID" | ||
|  |           value: "5617" | ||
|  |         > | ||
|  |         value: -0.0002956962622126468 | ||
|  |         timestamp: < | ||
|  |           seconds: 1625851150 | ||
|  |           nanos: 233181498 | ||
|  |         > | ||
|  |       > | ||
|  |     > | ||
|  |     sb_schema: 0 | ||
|  |     sb_zero_threshold: 0 | ||
|  |   > | ||
|  | > | ||
|  | 
 | ||
|  | `, | ||
|  | 		`name: "rpc_durations_seconds" | ||
|  | help: "RPC latency distributions." | ||
|  | type: SUMMARY | ||
|  | metric: < | ||
|  |   label: < | ||
|  |     name: "service" | ||
|  |     value: "exponential" | ||
|  |   > | ||
|  |   summary: < | ||
|  |     sample_count: 262 | ||
|  |     sample_sum: 0.00025551262820703587 | ||
|  |     quantile: < | ||
|  |       quantile: 0.5 | ||
|  |       value: 6.442786329648548e-07 | ||
|  |     > | ||
|  |     quantile: < | ||
|  |       quantile: 0.9 | ||
|  |       value: 1.9435742936658396e-06 | ||
|  |     > | ||
|  |     quantile: < | ||
|  |       quantile: 0.99 | ||
|  |       value: 4.0471608667037015e-06 | ||
|  |     > | ||
|  |   > | ||
|  | > | ||
|  | `, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	varintBuf := make([]byte, binary.MaxVarintLen32) | ||
|  | 	inputBuf := &bytes.Buffer{} | ||
|  | 
 | ||
|  | 	for _, tmf := range textMetricFamilies { | ||
|  | 		pb := &dto.MetricFamily{} | ||
|  | 		// From text to proto message.
 | ||
|  | 		require.NoError(t, proto.UnmarshalText(tmf, pb)) | ||
|  | 		// From proto message to binary protobuf.
 | ||
|  | 		protoBuf, err := proto.Marshal(pb) | ||
|  | 		require.NoError(t, err) | ||
|  | 
 | ||
|  | 		// Write first length, then binary protobuf.
 | ||
|  | 		varintLength := binary.PutUvarint(varintBuf, uint64(len(protoBuf))) | ||
|  | 		inputBuf.Write(varintBuf[:varintLength]) | ||
|  | 		inputBuf.Write(protoBuf) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	exp := []struct { | ||
|  | 		lset    labels.Labels | ||
|  | 		m       string | ||
|  | 		t       int64 | ||
|  | 		v       float64 | ||
|  | 		typ     MetricType | ||
|  | 		help    string | ||
|  | 		unit    string | ||
|  | 		comment string | ||
|  | 		shs     histogram.SparseHistogram | ||
|  | 		e       *exemplar.Exemplar | ||
|  | 	}{ | ||
|  | 		{ | ||
|  | 			m:    "go_build_info", | ||
|  | 			help: "Build information about the main Go module.", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:   "go_build_info", | ||
|  | 			typ: MetricTypeGauge, | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m: "go_build_info\xFFchecksum\xFF\xFFpath\xFFgithub.com/prometheus/client_golang\xFFversion\xFF(devel)", | ||
|  | 			v: 1, | ||
|  | 			lset: labels.FromStrings( | ||
|  | 				"__name__", "go_build_info", | ||
|  | 				"checksum", "", | ||
|  | 				"path", "github.com/prometheus/client_golang", | ||
|  | 				"version", "(devel)", | ||
|  | 			), | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:    "go_memstats_alloc_bytes_total", | ||
|  | 			help: "Total number of bytes allocated, even if freed.", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:   "go_memstats_alloc_bytes_total", | ||
|  | 			typ: MetricTypeCounter, | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m: "go_memstats_alloc_bytes_total", | ||
|  | 			v: 1.546544e+06, | ||
|  | 			lset: labels.FromStrings( | ||
|  | 				"__name__", "go_memstats_alloc_bytes_total", | ||
|  | 			), | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:    "something_untyped", | ||
|  | 			help: "Just to test the untyped type.", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:   "something_untyped", | ||
|  | 			typ: MetricTypeUnknown, | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m: "something_untyped", | ||
|  | 			t: 1234567, | ||
|  | 			v: 42, | ||
|  | 			lset: labels.FromStrings( | ||
|  | 				"__name__", "something_untyped", | ||
|  | 			), | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:    "test_histogram", | ||
|  | 			help: "Test histogram with many buckets removed to keep it manageable in size.", | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m:   "test_histogram", | ||
|  | 			typ: MetricTypeHistogram, | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			m: "test_histogram", | ||
|  | 			t: 1234568, | ||
|  | 			shs: histogram.SparseHistogram{ | ||
|  | 				Count:         175, | ||
|  | 				ZeroCount:     2, | ||
|  | 				Sum:           0.0008280461746287094, | ||
|  | 				ZeroThreshold: 2.938735877055719e-39, | ||
|  | 				Schema:        3, | ||
|  | 				PositiveSpans: []histogram.Span{ | ||
|  | 					{Offset: -161, Length: 1}, | ||
|  | 					{Offset: 8, Length: 3}, | ||
|  | 				}, | ||
|  | 				NegativeSpans: []histogram.Span{ | ||
|  | 					{Offset: -162, Length: 1}, | ||
|  | 					{Offset: 23, Length: 4}, | ||
|  | 				}, | ||
|  | 				PositiveBuckets: []int64{1, 2, -1, -1}, | ||
|  | 				NegativeBuckets: []int64{1, 3, -2, -1, 1}, | ||
|  | 			}, | ||
|  | 			lset: labels.FromStrings( | ||
|  | 				"__name__", "test_histogram", | ||
|  | 			), | ||
|  | 		}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	p := NewProtobufParser(inputBuf.Bytes()) | ||
|  | 	i := 0 | ||
|  | 
 | ||
|  | 	var res labels.Labels | ||
|  | 
 | ||
|  | 	for { | ||
|  | 		et, err := p.Next() | ||
|  | 		if err == io.EOF { | ||
|  | 			break | ||
|  | 		} | ||
|  | 		require.NoError(t, err) | ||
|  | 
 | ||
|  | 		switch et { | ||
|  | 		case EntrySeries: | ||
|  | 			m, ts, v := p.Series() | ||
|  | 
 | ||
|  | 			var e exemplar.Exemplar | ||
|  | 			p.Metric(&res) | ||
|  | 			found := p.Exemplar(&e) | ||
|  | 			require.Equal(t, exp[i].m, string(m)) | ||
|  | 			if ts != nil { | ||
|  | 				require.Equal(t, exp[i].t, *ts) | ||
|  | 			} else { | ||
|  | 				require.Equal(t, exp[i].t, int64(0)) | ||
|  | 			} | ||
|  | 			require.Equal(t, exp[i].v, v) | ||
|  | 			require.Equal(t, exp[i].lset, res) | ||
|  | 			if exp[i].e == nil { | ||
|  | 				require.Equal(t, false, found) | ||
|  | 			} else { | ||
|  | 				require.Equal(t, true, found) | ||
|  | 				require.Equal(t, *exp[i].e, e) | ||
|  | 			} | ||
|  | 			res = res[:0] | ||
|  | 
 | ||
|  | 		case EntryHistogram: | ||
|  | 			m, ts, shs := p.Histogram() | ||
|  | 
 | ||
|  | 			p.Metric(&res) | ||
|  | 			require.Equal(t, exp[i].m, string(m)) | ||
|  | 			if ts != nil { | ||
|  | 				require.Equal(t, exp[i].t, *ts) | ||
|  | 			} else { | ||
|  | 				require.Equal(t, exp[i].t, int64(0)) | ||
|  | 			} | ||
|  | 			require.Equal(t, exp[i].lset, res) | ||
|  | 			res = res[:0] | ||
|  | 			require.Equal(t, exp[i].m, string(m)) | ||
|  | 			require.Equal(t, exp[i].shs, shs) | ||
|  | 
 | ||
|  | 		case EntryType: | ||
|  | 			m, typ := p.Type() | ||
|  | 			require.Equal(t, exp[i].m, string(m)) | ||
|  | 			require.Equal(t, exp[i].typ, typ) | ||
|  | 
 | ||
|  | 		case EntryHelp: | ||
|  | 			m, h := p.Help() | ||
|  | 			require.Equal(t, exp[i].m, string(m)) | ||
|  | 			require.Equal(t, exp[i].help, string(h)) | ||
|  | 
 | ||
|  | 		case EntryUnit: | ||
|  | 			m, u := p.Unit() | ||
|  | 			require.Equal(t, exp[i].m, string(m)) | ||
|  | 			require.Equal(t, exp[i].unit, string(u)) | ||
|  | 
 | ||
|  | 		case EntryComment: | ||
|  | 			require.Equal(t, exp[i].comment, string(p.Comment())) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		i++ | ||
|  | 	} | ||
|  | 	// TODO(beorn7): Once supported by the parser, test exemplars for
 | ||
|  | 	// counters, exemplars for sparse histograms, legacy histograms including exemplars,
 | ||
|  | 	// summaries.
 | ||
|  | 	require.Equal(t, len(exp), i) | ||
|  | } |