mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Prepare for multiple text formats
Pass content type down to text parser. Add layer of indirection in front of text parser, and rename to avoid future clashes. Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
This commit is contained in:
parent
5824d6902d
commit
ffe7efb411
3
Makefile
3
Makefile
|
@ -17,8 +17,7 @@ STATICCHECK_IGNORE = \
|
||||||
github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:SA1019 \
|
github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:SA1019 \
|
||||||
github.com/prometheus/prometheus/discovery/kubernetes/node.go:SA1019 \
|
github.com/prometheus/prometheus/discovery/kubernetes/node.go:SA1019 \
|
||||||
github.com/prometheus/prometheus/documentation/examples/remote_storage/remote_storage_adapter/main.go:SA1019 \
|
github.com/prometheus/prometheus/documentation/examples/remote_storage/remote_storage_adapter/main.go:SA1019 \
|
||||||
github.com/prometheus/prometheus/pkg/textparse/lex.l.go:SA4006 \
|
github.com/prometheus/prometheus/pkg/textparse/promlex.l.go:SA4006 \
|
||||||
github.com/prometheus/prometheus/pkg/textparse/lex.l.go:SA4006 \
|
|
||||||
github.com/prometheus/prometheus/pkg/pool/pool.go:SA6002 \
|
github.com/prometheus/prometheus/pkg/pool/pool.go:SA6002 \
|
||||||
github.com/prometheus/prometheus/promql/engine.go:SA6002 \
|
github.com/prometheus/prometheus/promql/engine.go:SA6002 \
|
||||||
github.com/prometheus/prometheus/prompb/rpc.pb.gw.go:SA1019
|
github.com/prometheus/prometheus/prompb/rpc.pb.gw.go:SA1019
|
||||||
|
|
76
pkg/textparse/interface.go
Normal file
76
pkg/textparse/interface.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2017 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 (
|
||||||
|
"github.com/prometheus/prometheus/pkg/labels"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parser parses samples from a byte slice of samples in the official
|
||||||
|
// Prometheus text exposition format.
|
||||||
|
type Parser interface {
|
||||||
|
// Series returns the bytes of the series, the timestamp if set, and the value
|
||||||
|
// of the current sample.
|
||||||
|
Series() ([]byte, *int64, float64)
|
||||||
|
|
||||||
|
// Help returns the metric name and help text in the current entry.
|
||||||
|
// Must only be called after Next returned a help entry.
|
||||||
|
// The returned byte slices become invalid after the next call to Next.
|
||||||
|
Help() ([]byte, []byte)
|
||||||
|
|
||||||
|
// Type returns the metric name and type in the current entry.
|
||||||
|
// Must only be called after Next returned a type entry.
|
||||||
|
// The returned byte slices become invalid after the next call to Next.
|
||||||
|
Type() ([]byte, MetricType)
|
||||||
|
|
||||||
|
// Comment returns the text of the current comment.
|
||||||
|
// Must only be called after Next returned a comment entry.
|
||||||
|
// The returned byte slice becomes invalid after the next call to Next.
|
||||||
|
Comment() []byte
|
||||||
|
|
||||||
|
// Metric writes the labels of the current sample into the passed labels.
|
||||||
|
// It returns the string from which the metric was parsed.
|
||||||
|
Metric(l *labels.Labels) string
|
||||||
|
|
||||||
|
// Next advances the parser to the next sample. It returns false if no
|
||||||
|
// more samples were read or an error occurred.
|
||||||
|
Next() (Entry, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new parser of the byte slice.
|
||||||
|
func New(b []byte, contentType string) Parser {
|
||||||
|
return NewPromParser(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry represents the type of a parsed entry.
|
||||||
|
type Entry int
|
||||||
|
|
||||||
|
const (
|
||||||
|
EntryInvalid Entry = -1
|
||||||
|
EntryType Entry = 0
|
||||||
|
EntryHelp Entry = 1
|
||||||
|
EntrySeries Entry = 2
|
||||||
|
EntryComment Entry = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetricType represents metric type values.
|
||||||
|
type MetricType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MetricTypeCounter = "counter"
|
||||||
|
MetricTypeGauge = "gauge"
|
||||||
|
MetricTypeHistogram = "histogram"
|
||||||
|
MetricTypeSummary = "summary"
|
||||||
|
MetricTypeUntyped = "untyped"
|
||||||
|
)
|
|
@ -32,7 +32,7 @@ const (
|
||||||
// Lex is called by the parser generated by "go tool yacc" to obtain each
|
// Lex is called by the parser generated by "go tool yacc" to obtain each
|
||||||
// token. The method is opened before the matching rules block and closed at
|
// token. The method is opened before the matching rules block and closed at
|
||||||
// the end of the file.
|
// the end of the file.
|
||||||
func (l *lexer) Lex() token {
|
func (l *promlexer) Lex() token {
|
||||||
if l.i >= len(l.b) {
|
if l.i >= len(l.b) {
|
||||||
return tEOF
|
return tEOF
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ C [^\n]
|
||||||
return tInvalid
|
return tInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lexer) consumeComment() token {
|
func (l *promlexer) consumeComment() token {
|
||||||
for c := l.cur(); ; c = l.next() {
|
for c := l.cur(); ; c = l.next() {
|
||||||
switch c {
|
switch c {
|
||||||
case 0:
|
case 0:
|
|
@ -1,4 +1,4 @@
|
||||||
// CAUTION: Generated file - DO NOT EDIT.
|
// Code generated by golex. DO NOT EDIT.
|
||||||
|
|
||||||
// Copyright 2017 The Prometheus Authors
|
// Copyright 2017 The Prometheus Authors
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -33,7 +33,7 @@ const (
|
||||||
// Lex is called by the parser generated by "go tool yacc" to obtain each
|
// Lex is called by the parser generated by "go tool yacc" to obtain each
|
||||||
// token. The method is opened before the matching rules block and closed at
|
// token. The method is opened before the matching rules block and closed at
|
||||||
// the end of the file.
|
// the end of the file.
|
||||||
func (l *lexer) Lex() token {
|
func (l *promlexer) Lex() token {
|
||||||
if l.i >= len(l.b) {
|
if l.i >= len(l.b) {
|
||||||
return tEOF
|
return tEOF
|
||||||
}
|
}
|
||||||
|
@ -537,7 +537,7 @@ yyabort: // no lexem recognized
|
||||||
return tInvalid
|
return tInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lexer) consumeComment() token {
|
func (l *promlexer) consumeComment() token {
|
||||||
for c := l.cur(); ; c = l.next() {
|
for c := l.cur(); ; c = l.next() {
|
||||||
switch c {
|
switch c {
|
||||||
case 0:
|
case 0:
|
|
@ -12,9 +12,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//go:generate go get github.com/cznic/golex
|
//go:generate go get github.com/cznic/golex
|
||||||
//go:generate golex -o=lex.l.go lex.l
|
//go:generate golex -o=promlex.l.go promlex.l
|
||||||
|
|
||||||
// Package textparse contains an efficient parser for the Prometheus text format.
|
|
||||||
package textparse
|
package textparse
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -32,7 +31,7 @@ import (
|
||||||
"github.com/prometheus/prometheus/pkg/value"
|
"github.com/prometheus/prometheus/pkg/value"
|
||||||
)
|
)
|
||||||
|
|
||||||
type lexer struct {
|
type promlexer struct {
|
||||||
b []byte
|
b []byte
|
||||||
i int
|
i int
|
||||||
start int
|
start int
|
||||||
|
@ -106,16 +105,16 @@ func (t token) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// buf returns the buffer of the current token.
|
// buf returns the buffer of the current token.
|
||||||
func (l *lexer) buf() []byte {
|
func (l *promlexer) buf() []byte {
|
||||||
return l.b[l.start:l.i]
|
return l.b[l.start:l.i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lexer) cur() byte {
|
func (l *promlexer) cur() byte {
|
||||||
return l.b[l.i]
|
return l.b[l.i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// next advances the lexer to the next character.
|
// next advances the promlexer to the next character.
|
||||||
func (l *lexer) next() byte {
|
func (l *promlexer) next() byte {
|
||||||
l.i++
|
l.i++
|
||||||
if l.i >= len(l.b) {
|
if l.i >= len(l.b) {
|
||||||
l.err = io.EOF
|
l.err = io.EOF
|
||||||
|
@ -129,14 +128,14 @@ func (l *lexer) next() byte {
|
||||||
return l.b[l.i]
|
return l.b[l.i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *lexer) Error(es string) {
|
func (l *promlexer) Error(es string) {
|
||||||
l.err = errors.New(es)
|
l.err = errors.New(es)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser parses samples from a byte slice of samples in the official
|
// PromParser parses samples from a byte slice of samples in the official
|
||||||
// Prometheus text exposition format.
|
// Prometheus text exposition format.
|
||||||
type Parser struct {
|
type PromParser struct {
|
||||||
l *lexer
|
l *promlexer
|
||||||
series []byte
|
series []byte
|
||||||
text []byte
|
text []byte
|
||||||
mtype MetricType
|
mtype MetricType
|
||||||
|
@ -148,13 +147,13 @@ type Parser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new parser of the byte slice.
|
// New returns a new parser of the byte slice.
|
||||||
func New(b []byte) *Parser {
|
func NewPromParser(b []byte) Parser {
|
||||||
return &Parser{l: &lexer{b: append(b, '\n')}}
|
return &PromParser{l: &promlexer{b: append(b, '\n')}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Series returns the bytes of the series, the timestamp if set, and the value
|
// Series returns the bytes of the series, the timestamp if set, and the value
|
||||||
// of the current sample.
|
// of the current sample.
|
||||||
func (p *Parser) Series() ([]byte, *int64, float64) {
|
func (p *PromParser) Series() ([]byte, *int64, float64) {
|
||||||
if p.hasTS {
|
if p.hasTS {
|
||||||
return p.series, &p.ts, p.val
|
return p.series, &p.ts, p.val
|
||||||
}
|
}
|
||||||
|
@ -164,7 +163,7 @@ func (p *Parser) Series() ([]byte, *int64, float64) {
|
||||||
// Help returns the metric name and help text in the current entry.
|
// Help returns the metric name and help text in the current entry.
|
||||||
// Must only be called after Next returned a help entry.
|
// Must only be called after Next returned a help entry.
|
||||||
// The returned byte slices become invalid after the next call to Next.
|
// The returned byte slices become invalid after the next call to Next.
|
||||||
func (p *Parser) Help() ([]byte, []byte) {
|
func (p *PromParser) Help() ([]byte, []byte) {
|
||||||
m := p.l.b[p.offsets[0]:p.offsets[1]]
|
m := p.l.b[p.offsets[0]:p.offsets[1]]
|
||||||
|
|
||||||
// Replacer causes allocations. Replace only when necessary.
|
// Replacer causes allocations. Replace only when necessary.
|
||||||
|
@ -177,20 +176,20 @@ func (p *Parser) Help() ([]byte, []byte) {
|
||||||
// Type returns the metric name and type in the current entry.
|
// Type returns the metric name and type in the current entry.
|
||||||
// Must only be called after Next returned a type entry.
|
// Must only be called after Next returned a type entry.
|
||||||
// The returned byte slices become invalid after the next call to Next.
|
// The returned byte slices become invalid after the next call to Next.
|
||||||
func (p *Parser) Type() ([]byte, MetricType) {
|
func (p *PromParser) Type() ([]byte, MetricType) {
|
||||||
return p.l.b[p.offsets[0]:p.offsets[1]], p.mtype
|
return p.l.b[p.offsets[0]:p.offsets[1]], p.mtype
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comment returns the text of the current comment.
|
// Comment returns the text of the current comment.
|
||||||
// Must only be called after Next returned a comment entry.
|
// Must only be called after Next returned a comment entry.
|
||||||
// The returned byte slice becomes invalid after the next call to Next.
|
// The returned byte slice becomes invalid after the next call to Next.
|
||||||
func (p *Parser) Comment() []byte {
|
func (p *PromParser) Comment() []byte {
|
||||||
return p.text
|
return p.text
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metric writes the labels of the current sample into the passed labels.
|
// Metric writes the labels of the current sample into the passed labels.
|
||||||
// It returns the string from which the metric was parsed.
|
// It returns the string from which the metric was parsed.
|
||||||
func (p *Parser) Metric(l *labels.Labels) string {
|
func (p *PromParser) Metric(l *labels.Labels) string {
|
||||||
// Allocate the full immutable string immediately, so we just
|
// Allocate the full immutable string immediately, so we just
|
||||||
// have to create references on it below.
|
// have to create references on it below.
|
||||||
s := string(p.series)
|
s := string(p.series)
|
||||||
|
@ -221,9 +220,9 @@ func (p *Parser) Metric(l *labels.Labels) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// nextToken returns the next token from the lexer. It skips over tabs
|
// nextToken returns the next token from the promlexer. It skips over tabs
|
||||||
// and spaces.
|
// and spaces.
|
||||||
func (p *Parser) nextToken() token {
|
func (p *PromParser) nextToken() token {
|
||||||
for {
|
for {
|
||||||
if tok := p.l.Lex(); tok != tWhitespace {
|
if tok := p.l.Lex(); tok != tWhitespace {
|
||||||
return tok
|
return tok
|
||||||
|
@ -231,35 +230,13 @@ func (p *Parser) nextToken() token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry represents the type of a parsed entry.
|
|
||||||
type Entry int
|
|
||||||
|
|
||||||
const (
|
|
||||||
EntryInvalid Entry = -1
|
|
||||||
EntryType Entry = 0
|
|
||||||
EntryHelp Entry = 1
|
|
||||||
EntrySeries Entry = 2
|
|
||||||
EntryComment Entry = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// MetricType represents metric type values.
|
|
||||||
type MetricType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
MetricTypeCounter = "counter"
|
|
||||||
MetricTypeGauge = "gauge"
|
|
||||||
MetricTypeHistogram = "histogram"
|
|
||||||
MetricTypeSummary = "summary"
|
|
||||||
MetricTypeUntyped = "untyped"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseError(exp string, got token) error {
|
func parseError(exp string, got token) error {
|
||||||
return fmt.Errorf("%s, got %q", exp, got)
|
return fmt.Errorf("%s, got %q", exp, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next advances the parser to the next sample. It returns false if no
|
// Next advances the parser to the next sample. It returns false if no
|
||||||
// more samples were read or an error occurred.
|
// more samples were read or an error occurred.
|
||||||
func (p *Parser) Next() (Entry, error) {
|
func (p *PromParser) Next() (Entry, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
p.start = p.l.i
|
p.start = p.l.i
|
||||||
|
@ -371,7 +348,7 @@ func (p *Parser) Next() (Entry, error) {
|
||||||
return EntryInvalid, err
|
return EntryInvalid, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseLVals() error {
|
func (p *PromParser) parseLVals() error {
|
||||||
t := p.nextToken()
|
t := p.nextToken()
|
||||||
for {
|
for {
|
||||||
switch t {
|
switch t {
|
||||||
|
@ -393,7 +370,7 @@ func (p *Parser) parseLVals() error {
|
||||||
return fmt.Errorf("invalid UTF-8 label value")
|
return fmt.Errorf("invalid UTF-8 label value")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The lexer ensures the value string is quoted. Strip first
|
// The promlexer ensures the value string is quoted. Strip first
|
||||||
// and last character.
|
// and last character.
|
||||||
p.offsets = append(p.offsets, p.l.start+1, p.l.i-1)
|
p.offsets = append(p.offsets, p.l.start+1, p.l.i-1)
|
||||||
|
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestPromParse(t *testing.T) {
|
||||||
input := `# HELP go_gc_duration_seconds A summary of the GC invocation durations.
|
input := `# HELP go_gc_duration_seconds A summary of the GC invocation durations.
|
||||||
# TYPE go_gc_duration_seconds summary
|
# TYPE go_gc_duration_seconds summary
|
||||||
go_gc_duration_seconds{quantile="0"} 4.9351e-05
|
go_gc_duration_seconds{quantile="0"} 4.9351e-05
|
||||||
|
@ -164,7 +164,7 @@ testmetric{label="\"bar\""} 1`
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
p := New([]byte(input))
|
p := NewPromParser([]byte(input))
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
var res labels.Labels
|
var res labels.Labels
|
||||||
|
@ -207,7 +207,7 @@ testmetric{label="\"bar\""} 1`
|
||||||
require.Equal(t, len(exp), i)
|
require.Equal(t, len(exp), i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseErrors(t *testing.T) {
|
func TestPromParseErrors(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
input string
|
input string
|
||||||
err string
|
err string
|
||||||
|
@ -247,7 +247,7 @@ func TestParseErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
p := New([]byte(c.input))
|
p := NewPromParser([]byte(c.input))
|
||||||
var err error
|
var err error
|
||||||
for err == nil {
|
for err == nil {
|
||||||
_, err = p.Next()
|
_, err = p.Next()
|
||||||
|
@ -257,7 +257,7 @@ func TestParseErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNullByteHandling(t *testing.T) {
|
func TestPromNullByteHandling(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
input string
|
input string
|
||||||
err string
|
err string
|
||||||
|
@ -297,7 +297,7 @@ func TestNullByteHandling(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
p := New([]byte(c.input))
|
p := NewPromParser([]byte(c.input))
|
||||||
var err error
|
var err error
|
||||||
for err == nil {
|
for err == nil {
|
||||||
_, err = p.Next()
|
_, err = p.Next()
|
||||||
|
@ -314,11 +314,11 @@ func TestNullByteHandling(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testdataSampleCount = 410
|
promtestdataSampleCount = 410
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkParse(b *testing.B) {
|
func BenchmarkPromParse(b *testing.B) {
|
||||||
for _, fn := range []string{"testdata.txt", "testdata.nometa.txt"} {
|
for _, fn := range []string{"promtestdata.txt", "promtestdata.nometa.txt"} {
|
||||||
f, err := os.Open(fn)
|
f, err := os.Open(fn)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
@ -329,12 +329,12 @@ func BenchmarkParse(b *testing.B) {
|
||||||
b.Run("no-decode-metric/"+fn, func(b *testing.B) {
|
b.Run("no-decode-metric/"+fn, func(b *testing.B) {
|
||||||
total := 0
|
total := 0
|
||||||
|
|
||||||
b.SetBytes(int64(len(buf) * (b.N / testdataSampleCount)))
|
b.SetBytes(int64(len(buf) * (b.N / promtestdataSampleCount)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i += testdataSampleCount {
|
for i := 0; i < b.N; i += promtestdataSampleCount {
|
||||||
p := New(buf)
|
p := NewPromParser(buf)
|
||||||
|
|
||||||
Outer:
|
Outer:
|
||||||
for i < b.N {
|
for i < b.N {
|
||||||
|
@ -357,12 +357,12 @@ func BenchmarkParse(b *testing.B) {
|
||||||
b.Run("decode-metric/"+fn, func(b *testing.B) {
|
b.Run("decode-metric/"+fn, func(b *testing.B) {
|
||||||
total := 0
|
total := 0
|
||||||
|
|
||||||
b.SetBytes(int64(len(buf) * (b.N / testdataSampleCount)))
|
b.SetBytes(int64(len(buf) * (b.N / promtestdataSampleCount)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i += testdataSampleCount {
|
for i := 0; i < b.N; i += promtestdataSampleCount {
|
||||||
p := New(buf)
|
p := NewPromParser(buf)
|
||||||
|
|
||||||
Outer:
|
Outer:
|
||||||
for i < b.N {
|
for i < b.N {
|
||||||
|
@ -390,12 +390,12 @@ func BenchmarkParse(b *testing.B) {
|
||||||
total := 0
|
total := 0
|
||||||
res := make(labels.Labels, 0, 5)
|
res := make(labels.Labels, 0, 5)
|
||||||
|
|
||||||
b.SetBytes(int64(len(buf) * (b.N / testdataSampleCount)))
|
b.SetBytes(int64(len(buf) * (b.N / promtestdataSampleCount)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i += testdataSampleCount {
|
for i := 0; i < b.N; i += promtestdataSampleCount {
|
||||||
p := New(buf)
|
p := NewPromParser(buf)
|
||||||
|
|
||||||
Outer:
|
Outer:
|
||||||
for i < b.N {
|
for i < b.N {
|
||||||
|
@ -420,13 +420,13 @@ func BenchmarkParse(b *testing.B) {
|
||||||
_ = total
|
_ = total
|
||||||
})
|
})
|
||||||
b.Run("expfmt-text/"+fn, func(b *testing.B) {
|
b.Run("expfmt-text/"+fn, func(b *testing.B) {
|
||||||
b.SetBytes(int64(len(buf) * (b.N / testdataSampleCount)))
|
b.SetBytes(int64(len(buf) * (b.N / promtestdataSampleCount)))
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
total := 0
|
total := 0
|
||||||
|
|
||||||
for i := 0; i < b.N; i += testdataSampleCount {
|
for i := 0; i < b.N; i += promtestdataSampleCount {
|
||||||
var (
|
var (
|
||||||
decSamples = make(model.Vector, 0, 50)
|
decSamples = make(model.Vector, 0, 50)
|
||||||
)
|
)
|
||||||
|
@ -450,7 +450,7 @@ func BenchmarkParse(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func BenchmarkGzip(b *testing.B) {
|
func BenchmarkGzip(b *testing.B) {
|
||||||
for _, fn := range []string{"testdata.txt", "testdata.nometa.txt"} {
|
for _, fn := range []string{"promtestdata.txt", "promtestdata.nometa.txt"} {
|
||||||
b.Run(fn, func(b *testing.B) {
|
b.Run(fn, func(b *testing.B) {
|
||||||
f, err := os.Open(fn)
|
f, err := os.Open(fn)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
@ -466,7 +466,7 @@ func BenchmarkGzip(b *testing.B) {
|
||||||
gbuf, err := ioutil.ReadAll(&buf)
|
gbuf, err := ioutil.ReadAll(&buf)
|
||||||
require.NoError(b, err)
|
require.NoError(b, err)
|
||||||
|
|
||||||
k := b.N / testdataSampleCount
|
k := b.N / promtestdataSampleCount
|
||||||
|
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.SetBytes(int64(k) * int64(n))
|
b.SetBytes(int64(k) * int64(n))
|
|
@ -434,7 +434,7 @@ func appender(app storage.Appender, limit int) storage.Appender {
|
||||||
|
|
||||||
// A scraper retrieves samples and accepts a status report at the end.
|
// A scraper retrieves samples and accepts a status report at the end.
|
||||||
type scraper interface {
|
type scraper interface {
|
||||||
scrape(ctx context.Context, w io.Writer) error
|
scrape(ctx context.Context, w io.Writer) (string, error)
|
||||||
report(start time.Time, dur time.Duration, err error)
|
report(start time.Time, dur time.Duration, err error)
|
||||||
offset(interval time.Duration) time.Duration
|
offset(interval time.Duration) time.Duration
|
||||||
}
|
}
|
||||||
|
@ -455,11 +455,11 @@ const acceptHeader = `text/plain;version=0.0.4;q=1,*/*;q=0.1`
|
||||||
|
|
||||||
var userAgentHeader = fmt.Sprintf("Prometheus/%s", version.Version)
|
var userAgentHeader = fmt.Sprintf("Prometheus/%s", version.Version)
|
||||||
|
|
||||||
func (s *targetScraper) scrape(ctx context.Context, w io.Writer) error {
|
func (s *targetScraper) scrape(ctx context.Context, w io.Writer) (string, error) {
|
||||||
if s.req == nil {
|
if s.req == nil {
|
||||||
req, err := http.NewRequest("GET", s.URL().String(), nil)
|
req, err := http.NewRequest("GET", s.URL().String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
req.Header.Add("Accept", acceptHeader)
|
req.Header.Add("Accept", acceptHeader)
|
||||||
req.Header.Add("Accept-Encoding", "gzip")
|
req.Header.Add("Accept-Encoding", "gzip")
|
||||||
|
@ -471,35 +471,38 @@ func (s *targetScraper) scrape(ctx context.Context, w io.Writer) error {
|
||||||
|
|
||||||
resp, err := ctxhttp.Do(ctx, s.client, s.req)
|
resp, err := ctxhttp.Do(ctx, s.client, s.req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("server returned HTTP status %s", resp.Status)
|
return "", fmt.Errorf("server returned HTTP status %s", resp.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Header.Get("Content-Encoding") != "gzip" {
|
if resp.Header.Get("Content-Encoding") != "gzip" {
|
||||||
_, err = io.Copy(w, resp.Body)
|
_, err = io.Copy(w, resp.Body)
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.gzipr == nil {
|
if s.gzipr == nil {
|
||||||
s.buf = bufio.NewReader(resp.Body)
|
s.buf = bufio.NewReader(resp.Body)
|
||||||
s.gzipr, err = gzip.NewReader(s.buf)
|
s.gzipr, err = gzip.NewReader(s.buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.buf.Reset(resp.Body)
|
s.buf.Reset(resp.Body)
|
||||||
if err = s.gzipr.Reset(s.buf); err != nil {
|
if err = s.gzipr.Reset(s.buf); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.Copy(w, s.gzipr)
|
_, err = io.Copy(w, s.gzipr)
|
||||||
s.gzipr.Close()
|
s.gzipr.Close()
|
||||||
return err
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return resp.Header.Get("Content-Type"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A loop can run and be stopped again. It must not be reused after it was stopped.
|
// A loop can run and be stopped again. It must not be reused after it was stopped.
|
||||||
|
@ -789,7 +792,7 @@ mainLoop:
|
||||||
b := sl.buffers.Get(sl.lastScrapeSize).([]byte)
|
b := sl.buffers.Get(sl.lastScrapeSize).([]byte)
|
||||||
buf := bytes.NewBuffer(b)
|
buf := bytes.NewBuffer(b)
|
||||||
|
|
||||||
scrapeErr := sl.scraper.scrape(scrapeCtx, buf)
|
contentType, scrapeErr := sl.scraper.scrape(scrapeCtx, buf)
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
if scrapeErr == nil {
|
if scrapeErr == nil {
|
||||||
|
@ -809,12 +812,12 @@ mainLoop:
|
||||||
|
|
||||||
// A failed scrape is the same as an empty scrape,
|
// A failed scrape is the same as an empty scrape,
|
||||||
// we still call sl.append to trigger stale markers.
|
// we still call sl.append to trigger stale markers.
|
||||||
total, added, appErr := sl.append(b, start)
|
total, added, appErr := sl.append(b, contentType, start)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
level.Warn(sl.l).Log("msg", "append failed", "err", appErr)
|
level.Warn(sl.l).Log("msg", "append failed", "err", appErr)
|
||||||
// The append failed, probably due to a parse error or sample limit.
|
// The append failed, probably due to a parse error or sample limit.
|
||||||
// Call sl.append again with an empty scrape to trigger stale markers.
|
// Call sl.append again with an empty scrape to trigger stale markers.
|
||||||
if _, _, err := sl.append([]byte{}, start); err != nil {
|
if _, _, err := sl.append([]byte{}, "", start); err != nil {
|
||||||
level.Warn(sl.l).Log("msg", "append failed", "err", err)
|
level.Warn(sl.l).Log("msg", "append failed", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,7 +888,7 @@ func (sl *scrapeLoop) endOfRunStaleness(last time.Time, ticker *time.Ticker, int
|
||||||
// Call sl.append again with an empty scrape to trigger stale markers.
|
// Call sl.append again with an empty scrape to trigger stale markers.
|
||||||
// If the target has since been recreated and scraped, the
|
// If the target has since been recreated and scraped, the
|
||||||
// stale markers will be out of order and ignored.
|
// stale markers will be out of order and ignored.
|
||||||
if _, _, err := sl.append([]byte{}, staleTime); err != nil {
|
if _, _, err := sl.append([]byte{}, "", staleTime); err != nil {
|
||||||
level.Error(sl.l).Log("msg", "stale append failed", "err", err)
|
level.Error(sl.l).Log("msg", "stale append failed", "err", err)
|
||||||
}
|
}
|
||||||
if err := sl.reportStale(staleTime); err != nil {
|
if err := sl.reportStale(staleTime); err != nil {
|
||||||
|
@ -921,10 +924,10 @@ func (s samples) Less(i, j int) bool {
|
||||||
return s[i].t < s[j].t
|
return s[i].t < s[j].t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl *scrapeLoop) append(b []byte, ts time.Time) (total, added int, err error) {
|
func (sl *scrapeLoop) append(b []byte, contentType string, ts time.Time) (total, added int, err error) {
|
||||||
var (
|
var (
|
||||||
app = sl.appender()
|
app = sl.appender()
|
||||||
p = textparse.New(b)
|
p = textparse.New(b, contentType)
|
||||||
defTime = timestamp.FromTime(ts)
|
defTime = timestamp.FromTime(ts)
|
||||||
numOutOfOrder = 0
|
numOutOfOrder = 0
|
||||||
numDuplicates = 0
|
numDuplicates = 0
|
||||||
|
|
|
@ -621,7 +621,7 @@ func TestScrapeLoopMetadata(t *testing.T) {
|
||||||
# other comment
|
# other comment
|
||||||
test_metric 1
|
test_metric 1
|
||||||
# TYPE test_metric_no_help gauge
|
# TYPE test_metric_no_help gauge
|
||||||
# HELP test_metric_no_type other help text`), time.Now())
|
# HELP test_metric_no_type other help text`), "", time.Now())
|
||||||
testutil.Ok(t, err)
|
testutil.Ok(t, err)
|
||||||
testutil.Equals(t, 1, total)
|
testutil.Equals(t, 1, total)
|
||||||
|
|
||||||
|
@ -824,7 +824,7 @@ func TestScrapeLoopAppend(t *testing.T) {
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
_, _, err := sl.append([]byte(test.scrapeLabels), now)
|
_, _, err := sl.append([]byte(test.scrapeLabels), "", now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -870,7 +870,7 @@ func TestScrapeLoopAppendSampleLimit(t *testing.T) {
|
||||||
beforeMetricValue := beforeMetric.GetCounter().GetValue()
|
beforeMetricValue := beforeMetric.GetCounter().GetValue()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
_, _, err = sl.append([]byte("metric_a 1\nmetric_b 1\nmetric_c 1\n"), now)
|
_, _, err = sl.append([]byte("metric_a 1\nmetric_b 1\nmetric_c 1\n"), "", now)
|
||||||
if err != errSampleLimit {
|
if err != errSampleLimit {
|
||||||
t.Fatalf("Did not see expected sample limit error: %s", err)
|
t.Fatalf("Did not see expected sample limit error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -922,11 +922,11 @@ func TestScrapeLoop_ChangingMetricString(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
_, _, err = sl.append([]byte(`metric_a{a="1",b="1"} 1`), now)
|
_, _, err = sl.append([]byte(`metric_a{a="1",b="1"} 1`), "", now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
_, _, err = sl.append([]byte(`metric_a{b="1",a="1"} 2`), now.Add(time.Minute))
|
_, _, err = sl.append([]byte(`metric_a{b="1",a="1"} 2`), "", now.Add(time.Minute))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -961,11 +961,11 @@ func TestScrapeLoopAppendStaleness(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
_, _, err := sl.append([]byte("metric_a 1\n"), now)
|
_, _, err := sl.append([]byte("metric_a 1\n"), "", now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
_, _, err = sl.append([]byte(""), now.Add(time.Second))
|
_, _, err = sl.append([]byte(""), "", now.Add(time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1006,11 +1006,11 @@ func TestScrapeLoopAppendNoStalenessIfTimestamp(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
_, _, err := sl.append([]byte("metric_a 1 1000\n"), now)
|
_, _, err := sl.append([]byte("metric_a 1 1000\n"), "", now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
_, _, err = sl.append([]byte(""), now.Add(time.Second))
|
_, _, err = sl.append([]byte(""), "", now.Add(time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1120,7 +1120,7 @@ func TestScrapeLoopAppendGracefullyIfAmendOrOutOfOrderOrOutOfBounds(t *testing.T
|
||||||
)
|
)
|
||||||
|
|
||||||
now := time.Unix(1, 0)
|
now := time.Unix(1, 0)
|
||||||
_, _, err := sl.append([]byte("out_of_order 1\namend 1\nnormal 1\nout_of_bounds 1\n"), now)
|
_, _, err := sl.append([]byte("out_of_order 1\namend 1\nnormal 1\nout_of_bounds 1\n"), "", now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected append error: %s", err)
|
t.Fatalf("Unexpected append error: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1153,7 +1153,7 @@ func TestScrapeLoopOutOfBoundsTimeError(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
now := time.Now().Add(20 * time.Minute)
|
now := time.Now().Add(20 * time.Minute)
|
||||||
total, added, err := sl.append([]byte("normal 1\n"), now)
|
total, added, err := sl.append([]byte("normal 1\n"), "", now)
|
||||||
if total != 1 {
|
if total != 1 {
|
||||||
t.Error("expected 1 metric")
|
t.Error("expected 1 metric")
|
||||||
return
|
return
|
||||||
|
@ -1209,7 +1209,7 @@ func TestTargetScraperScrapeOK(t *testing.T) {
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
if err := ts.scrape(context.Background(), &buf); err != nil {
|
if _, err := ts.scrape(context.Background(), &buf); err != nil {
|
||||||
t.Fatalf("Unexpected scrape error: %s", err)
|
t.Fatalf("Unexpected scrape error: %s", err)
|
||||||
}
|
}
|
||||||
require.Equal(t, "metric_a 1\nmetric_b 2\n", buf.String())
|
require.Equal(t, "metric_a 1\nmetric_b 2\n", buf.String())
|
||||||
|
@ -1249,7 +1249,7 @@ func TestTargetScrapeScrapeCancel(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := ts.scrape(ctx, ioutil.Discard); err != context.Canceled {
|
if _, err := ts.scrape(ctx, ioutil.Discard); err != context.Canceled {
|
||||||
errc <- fmt.Errorf("Expected context cancelation error but got: %s", err)
|
errc <- fmt.Errorf("Expected context cancelation error but got: %s", err)
|
||||||
}
|
}
|
||||||
close(errc)
|
close(errc)
|
||||||
|
@ -1291,7 +1291,7 @@ func TestTargetScrapeScrapeNotFound(t *testing.T) {
|
||||||
client: http.DefaultClient,
|
client: http.DefaultClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ts.scrape(context.Background(), ioutil.Discard); !strings.Contains(err.Error(), "404") {
|
if _, err := ts.scrape(context.Background(), ioutil.Discard); !strings.Contains(err.Error(), "404") {
|
||||||
t.Fatalf("Expected \"404 NotFound\" error but got: %s", err)
|
t.Fatalf("Expected \"404 NotFound\" error but got: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1319,9 +1319,9 @@ func (ts *testScraper) report(start time.Time, duration time.Duration, err error
|
||||||
ts.lastError = err
|
ts.lastError = err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *testScraper) scrape(ctx context.Context, w io.Writer) error {
|
func (ts *testScraper) scrape(ctx context.Context, w io.Writer) (string, error) {
|
||||||
if ts.scrapeFunc != nil {
|
if ts.scrapeFunc != nil {
|
||||||
return ts.scrapeFunc(ctx, w)
|
return "", ts.scrapeFunc(ctx, w)
|
||||||
}
|
}
|
||||||
return ts.scrapeErr
|
return "", ts.scrapeErr
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue