Update dependencies
This commit is contained in:
73
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
73
vendor/github.com/prometheus/common/expfmt/decode.go
generated
vendored
@@ -14,6 +14,7 @@
|
||||
package expfmt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
@@ -21,8 +22,8 @@ import (
|
||||
"net/http"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/encoding/protodelim"
|
||||
|
||||
"github.com/matttproud/golang_protobuf_extensions/pbutil"
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
@@ -69,28 +70,45 @@ func ResponseFormat(h http.Header) Format {
|
||||
return FmtUnknown
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder based on the given input format.
|
||||
// If the input format does not imply otherwise, a text format decoder is returned.
|
||||
// NewDecoder returns a new decoder based on the given input format. Metric
|
||||
// names are validated based on the provided Format -- if the format requires
|
||||
// escaping, raditional Prometheues validity checking is used. Otherwise, names
|
||||
// are checked for UTF-8 validity. Supported formats include delimited protobuf
|
||||
// and Prometheus text format. For historical reasons, this decoder fallbacks
|
||||
// to classic text decoding for any other format. This decoder does not fully
|
||||
// support OpenMetrics although it may often succeed due to the similarities
|
||||
// between the formats. This decoder may not support the latest features of
|
||||
// Prometheus text format and is not intended for high-performance applications.
|
||||
// See: https://github.com/prometheus/common/issues/812
|
||||
func NewDecoder(r io.Reader, format Format) Decoder {
|
||||
switch format {
|
||||
case FmtProtoDelim:
|
||||
return &protoDecoder{r: r}
|
||||
scheme := model.LegacyValidation
|
||||
if format.ToEscapingScheme() == model.NoEscaping {
|
||||
scheme = model.UTF8Validation
|
||||
}
|
||||
return &textDecoder{r: r}
|
||||
switch format.FormatType() {
|
||||
case TypeProtoDelim:
|
||||
return &protoDecoder{r: bufio.NewReader(r), s: scheme}
|
||||
case TypeProtoText, TypeProtoCompact:
|
||||
return &errDecoder{err: fmt.Errorf("format %s not supported for decoding", format)}
|
||||
}
|
||||
return &textDecoder{r: r, s: scheme}
|
||||
}
|
||||
|
||||
// protoDecoder implements the Decoder interface for protocol buffers.
|
||||
type protoDecoder struct {
|
||||
r io.Reader
|
||||
r protodelim.Reader
|
||||
s model.ValidationScheme
|
||||
}
|
||||
|
||||
// Decode implements the Decoder interface.
|
||||
func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||
_, err := pbutil.ReadDelimited(d.r, v)
|
||||
if err != nil {
|
||||
opts := protodelim.UnmarshalOptions{
|
||||
MaxSize: -1,
|
||||
}
|
||||
if err := opts.UnmarshalFrom(d.r, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if !model.IsValidMetricName(model.LabelValue(v.GetName())) {
|
||||
if !d.s.IsValidMetricName(v.GetName()) {
|
||||
return fmt.Errorf("invalid metric name %q", v.GetName())
|
||||
}
|
||||
for _, m := range v.GetMetric() {
|
||||
@@ -104,7 +122,7 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||
if !model.LabelValue(l.GetValue()).IsValid() {
|
||||
return fmt.Errorf("invalid label value %q", l.GetValue())
|
||||
}
|
||||
if !model.LabelName(l.GetName()).IsValid() {
|
||||
if !d.s.IsValidLabelName(l.GetName()) {
|
||||
return fmt.Errorf("invalid label name %q", l.GetName())
|
||||
}
|
||||
}
|
||||
@@ -112,10 +130,20 @@ func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// errDecoder is an error-state decoder that always returns the same error.
|
||||
type errDecoder struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (d *errDecoder) Decode(*dto.MetricFamily) error {
|
||||
return d.err
|
||||
}
|
||||
|
||||
// textDecoder implements the Decoder interface for the text protocol.
|
||||
type textDecoder struct {
|
||||
r io.Reader
|
||||
fams map[string]*dto.MetricFamily
|
||||
s model.ValidationScheme
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -123,7 +151,7 @@ type textDecoder struct {
|
||||
func (d *textDecoder) Decode(v *dto.MetricFamily) error {
|
||||
if d.err == nil {
|
||||
// Read all metrics in one shot.
|
||||
var p TextParser
|
||||
p := NewTextParser(d.s)
|
||||
d.fams, d.err = p.TextToMetricFamilies(d.r)
|
||||
// If we don't get an error, store io.EOF for the end.
|
||||
if d.err == nil {
|
||||
@@ -132,7 +160,10 @@ func (d *textDecoder) Decode(v *dto.MetricFamily) error {
|
||||
}
|
||||
// Pick off one MetricFamily per Decode until there's nothing left.
|
||||
for key, fam := range d.fams {
|
||||
*v = *fam
|
||||
v.Name = fam.Name
|
||||
v.Help = fam.Help
|
||||
v.Type = fam.Type
|
||||
v.Metric = fam.Metric
|
||||
delete(d.fams, key)
|
||||
return nil
|
||||
}
|
||||
@@ -189,7 +220,7 @@ func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error)
|
||||
return extractSummary(o, f), nil
|
||||
case dto.MetricType_UNTYPED:
|
||||
return extractUntyped(o, f), nil
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
return extractHistogram(o, f), nil
|
||||
}
|
||||
return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
|
||||
@@ -372,9 +403,13 @@ func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||
infSeen = true
|
||||
}
|
||||
|
||||
v := q.GetCumulativeCountFloat()
|
||||
if v <= 0 {
|
||||
v = float64(q.GetCumulativeCount())
|
||||
}
|
||||
samples = append(samples, &model.Sample{
|
||||
Metric: model.Metric(lset),
|
||||
Value: model.SampleValue(q.GetCumulativeCount()),
|
||||
Value: model.SampleValue(v),
|
||||
Timestamp: timestamp,
|
||||
})
|
||||
}
|
||||
@@ -397,9 +432,13 @@ func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
|
||||
}
|
||||
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
|
||||
|
||||
v := m.Histogram.GetSampleCountFloat()
|
||||
if v <= 0 {
|
||||
v = float64(m.Histogram.GetSampleCount())
|
||||
}
|
||||
count := &model.Sample{
|
||||
Metric: model.Metric(lset),
|
||||
Value: model.SampleValue(m.Histogram.GetSampleCount()),
|
||||
Value: model.SampleValue(v),
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
samples = append(samples, count)
|
||||
|
||||
92
vendor/github.com/prometheus/common/expfmt/encode.go
generated
vendored
92
vendor/github.com/prometheus/common/expfmt/encode.go
generated
vendored
@@ -18,11 +18,12 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/matttproud/golang_protobuf_extensions/pbutil"
|
||||
"github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg"
|
||||
|
||||
"github.com/munnerz/goautoneg"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/encoding/protodelim"
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
// Encoder types encode metric families into an underlying wire protocol.
|
||||
@@ -58,25 +59,34 @@ func (ec encoderCloser) Close() error {
|
||||
// appropriate accepted type is found, FmtText is returned (which is the
|
||||
// Prometheus text format). This function will never negotiate FmtOpenMetrics,
|
||||
// as the support is still experimental. To include the option to negotiate
|
||||
// FmtOpenMetrics, use NegotiateOpenMetrics.
|
||||
// FmtOpenMetrics, use NegotiateIncludingOpenMetrics.
|
||||
func Negotiate(h http.Header) Format {
|
||||
escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String())))
|
||||
for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
|
||||
if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" {
|
||||
switch Format(escapeParam) {
|
||||
case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues:
|
||||
escapingScheme = Format("; escaping=" + escapeParam)
|
||||
default:
|
||||
// If the escaping parameter is unknown, ignore it.
|
||||
}
|
||||
}
|
||||
ver := ac.Params["version"]
|
||||
if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
|
||||
switch ac.Params["encoding"] {
|
||||
case "delimited":
|
||||
return FmtProtoDelim
|
||||
return FmtProtoDelim + escapingScheme
|
||||
case "text":
|
||||
return FmtProtoText
|
||||
return FmtProtoText + escapingScheme
|
||||
case "compact-text":
|
||||
return FmtProtoCompact
|
||||
return FmtProtoCompact + escapingScheme
|
||||
}
|
||||
}
|
||||
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
|
||||
return FmtText
|
||||
return FmtText + escapingScheme
|
||||
}
|
||||
}
|
||||
return FmtText
|
||||
return FmtText + escapingScheme
|
||||
}
|
||||
|
||||
// NegotiateIncludingOpenMetrics works like Negotiate but includes
|
||||
@@ -84,26 +94,40 @@ func Negotiate(h http.Header) Format {
|
||||
// temporary and will disappear once FmtOpenMetrics is fully supported and as
|
||||
// such may be negotiated by the normal Negotiate function.
|
||||
func NegotiateIncludingOpenMetrics(h http.Header) Format {
|
||||
escapingScheme := Format(fmt.Sprintf("; escaping=%s", Format(model.NameEscapingScheme.String())))
|
||||
for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
|
||||
if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" {
|
||||
switch Format(escapeParam) {
|
||||
case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues:
|
||||
escapingScheme = Format("; escaping=" + escapeParam)
|
||||
default:
|
||||
// If the escaping parameter is unknown, ignore it.
|
||||
}
|
||||
}
|
||||
ver := ac.Params["version"]
|
||||
if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
|
||||
switch ac.Params["encoding"] {
|
||||
case "delimited":
|
||||
return FmtProtoDelim
|
||||
return FmtProtoDelim + escapingScheme
|
||||
case "text":
|
||||
return FmtProtoText
|
||||
return FmtProtoText + escapingScheme
|
||||
case "compact-text":
|
||||
return FmtProtoCompact
|
||||
return FmtProtoCompact + escapingScheme
|
||||
}
|
||||
}
|
||||
if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
|
||||
return FmtText
|
||||
return FmtText + escapingScheme
|
||||
}
|
||||
if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion || ver == "") {
|
||||
return FmtOpenMetrics
|
||||
if ac.Type+"/"+ac.SubType == OpenMetricsType && (ver == OpenMetricsVersion_0_0_1 || ver == OpenMetricsVersion_1_0_0 || ver == "") {
|
||||
switch ver {
|
||||
case OpenMetricsVersion_1_0_0:
|
||||
return FmtOpenMetrics_1_0_0 + escapingScheme
|
||||
default:
|
||||
return FmtOpenMetrics_0_0_1 + escapingScheme
|
||||
}
|
||||
}
|
||||
}
|
||||
return FmtText
|
||||
return FmtText + escapingScheme
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder based on content type negotiation. All
|
||||
@@ -112,44 +136,54 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format {
|
||||
// for FmtOpenMetrics, but a future (breaking) release will add the Close method
|
||||
// to the Encoder interface directly. The current version of the Encoder
|
||||
// interface is kept for backwards compatibility.
|
||||
func NewEncoder(w io.Writer, format Format) Encoder {
|
||||
switch format {
|
||||
case FmtProtoDelim:
|
||||
// In cases where the Format does not allow for UTF-8 names, the global
|
||||
// NameEscapingScheme will be applied.
|
||||
//
|
||||
// NewEncoder can be called with additional options to customize the OpenMetrics text output.
|
||||
// For example:
|
||||
// NewEncoder(w, FmtOpenMetrics_1_0_0, WithCreatedLines())
|
||||
//
|
||||
// Extra options are ignored for all other formats.
|
||||
func NewEncoder(w io.Writer, format Format, options ...EncoderOption) Encoder {
|
||||
escapingScheme := format.ToEscapingScheme()
|
||||
|
||||
switch format.FormatType() {
|
||||
case TypeProtoDelim:
|
||||
return encoderCloser{
|
||||
encode: func(v *dto.MetricFamily) error {
|
||||
_, err := pbutil.WriteDelimited(w, v)
|
||||
_, err := protodelim.MarshalTo(w, model.EscapeMetricFamily(v, escapingScheme))
|
||||
return err
|
||||
},
|
||||
close: func() error { return nil },
|
||||
}
|
||||
case FmtProtoCompact:
|
||||
case TypeProtoCompact:
|
||||
return encoderCloser{
|
||||
encode: func(v *dto.MetricFamily) error {
|
||||
_, err := fmt.Fprintln(w, v.String())
|
||||
_, err := fmt.Fprintln(w, model.EscapeMetricFamily(v, escapingScheme).String())
|
||||
return err
|
||||
},
|
||||
close: func() error { return nil },
|
||||
}
|
||||
case FmtProtoText:
|
||||
case TypeProtoText:
|
||||
return encoderCloser{
|
||||
encode: func(v *dto.MetricFamily) error {
|
||||
_, err := fmt.Fprintln(w, proto.MarshalTextString(v))
|
||||
_, err := fmt.Fprintln(w, prototext.Format(model.EscapeMetricFamily(v, escapingScheme)))
|
||||
return err
|
||||
},
|
||||
close: func() error { return nil },
|
||||
}
|
||||
case FmtText:
|
||||
case TypeTextPlain:
|
||||
return encoderCloser{
|
||||
encode: func(v *dto.MetricFamily) error {
|
||||
_, err := MetricFamilyToText(w, v)
|
||||
_, err := MetricFamilyToText(w, model.EscapeMetricFamily(v, escapingScheme))
|
||||
return err
|
||||
},
|
||||
close: func() error { return nil },
|
||||
}
|
||||
case FmtOpenMetrics:
|
||||
case TypeOpenMetrics:
|
||||
return encoderCloser{
|
||||
encode: func(v *dto.MetricFamily) error {
|
||||
_, err := MetricFamilyToOpenMetrics(w, v)
|
||||
_, err := MetricFamilyToOpenMetrics(w, model.EscapeMetricFamily(v, escapingScheme), options...)
|
||||
return err
|
||||
},
|
||||
close: func() error {
|
||||
|
||||
197
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
197
vendor/github.com/prometheus/common/expfmt/expfmt.go
generated
vendored
@@ -14,28 +14,199 @@
|
||||
// Package expfmt contains tools for reading and writing Prometheus metrics.
|
||||
package expfmt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
// Format specifies the HTTP content type of the different wire protocols.
|
||||
type Format string
|
||||
|
||||
// Constants to assemble the Content-Type values for the different wire protocols.
|
||||
// Constants to assemble the Content-Type values for the different wire
|
||||
// protocols. The Content-Type strings here are all for the legacy exposition
|
||||
// formats, where valid characters for metric names and label names are limited.
|
||||
// Support for arbitrary UTF-8 characters in those names is already partially
|
||||
// implemented in this module (see model.ValidationScheme), but to actually use
|
||||
// it on the wire, new content-type strings will have to be agreed upon and
|
||||
// added here.
|
||||
const (
|
||||
TextVersion = "0.0.4"
|
||||
ProtoType = `application/vnd.google.protobuf`
|
||||
ProtoProtocol = `io.prometheus.client.MetricFamily`
|
||||
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
|
||||
OpenMetricsType = `application/openmetrics-text`
|
||||
OpenMetricsVersion = "0.0.1"
|
||||
TextVersion = "0.0.4"
|
||||
ProtoType = `application/vnd.google.protobuf`
|
||||
ProtoProtocol = `io.prometheus.client.MetricFamily`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeProtoCompact) instead.
|
||||
ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
|
||||
OpenMetricsType = `application/openmetrics-text`
|
||||
//nolint:revive // Allow for underscores.
|
||||
OpenMetricsVersion_0_0_1 = "0.0.1"
|
||||
//nolint:revive // Allow for underscores.
|
||||
OpenMetricsVersion_1_0_0 = "1.0.0"
|
||||
|
||||
// The Content-Type values for the different wire protocols.
|
||||
FmtUnknown Format = `<unknown>`
|
||||
FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
|
||||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
||||
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
||||
// The Content-Type values for the different wire protocols. Do not do direct
|
||||
// comparisons to these constants, instead use the comparison functions.
|
||||
//
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeUnknown) instead.
|
||||
FmtUnknown Format = `<unknown>`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeTextPlain) instead.
|
||||
FmtText Format = `text/plain; version=` + TextVersion + `; charset=utf-8`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeProtoDelim) instead.
|
||||
FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeProtoText) instead.
|
||||
FmtProtoText Format = ProtoFmt + ` encoding=text`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeProtoCompact) instead.
|
||||
FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
|
||||
FmtOpenMetrics Format = OpenMetricsType + `; version=` + OpenMetricsVersion + `; charset=utf-8`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeOpenMetrics) instead.
|
||||
//nolint:revive // Allow for underscores.
|
||||
FmtOpenMetrics_1_0_0 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_1_0_0 + `; charset=utf-8`
|
||||
// Deprecated: Use expfmt.NewFormat(expfmt.TypeOpenMetrics) instead.
|
||||
//nolint:revive // Allow for underscores.
|
||||
FmtOpenMetrics_0_0_1 Format = OpenMetricsType + `; version=` + OpenMetricsVersion_0_0_1 + `; charset=utf-8`
|
||||
)
|
||||
|
||||
const (
|
||||
hdrContentType = "Content-Type"
|
||||
hdrAccept = "Accept"
|
||||
)
|
||||
|
||||
// FormatType is a Go enum representing the overall category for the given
|
||||
// Format. As the number of Format permutations increases, doing basic string
|
||||
// comparisons are not feasible, so this enum captures the most useful
|
||||
// high-level attribute of the Format string.
|
||||
type FormatType int
|
||||
|
||||
const (
|
||||
TypeUnknown FormatType = iota
|
||||
TypeProtoCompact
|
||||
TypeProtoDelim
|
||||
TypeProtoText
|
||||
TypeTextPlain
|
||||
TypeOpenMetrics
|
||||
)
|
||||
|
||||
// NewFormat generates a new Format from the type provided. Mostly used for
|
||||
// tests, most Formats should be generated as part of content negotiation in
|
||||
// encode.go. If a type has more than one version, the latest version will be
|
||||
// returned.
|
||||
func NewFormat(t FormatType) Format {
|
||||
switch t {
|
||||
case TypeProtoCompact:
|
||||
return FmtProtoCompact
|
||||
case TypeProtoDelim:
|
||||
return FmtProtoDelim
|
||||
case TypeProtoText:
|
||||
return FmtProtoText
|
||||
case TypeTextPlain:
|
||||
return FmtText
|
||||
case TypeOpenMetrics:
|
||||
return FmtOpenMetrics_1_0_0
|
||||
default:
|
||||
return FmtUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// NewOpenMetricsFormat generates a new OpenMetrics format matching the
|
||||
// specified version number.
|
||||
func NewOpenMetricsFormat(version string) (Format, error) {
|
||||
if version == OpenMetricsVersion_0_0_1 {
|
||||
return FmtOpenMetrics_0_0_1, nil
|
||||
}
|
||||
if version == OpenMetricsVersion_1_0_0 {
|
||||
return FmtOpenMetrics_1_0_0, nil
|
||||
}
|
||||
return FmtUnknown, errors.New("unknown open metrics version string")
|
||||
}
|
||||
|
||||
// WithEscapingScheme returns a copy of Format with the specified escaping
|
||||
// scheme appended to the end. If an escaping scheme already exists it is
|
||||
// removed.
|
||||
func (f Format) WithEscapingScheme(s model.EscapingScheme) Format {
|
||||
var terms []string
|
||||
for _, p := range strings.Split(string(f), ";") {
|
||||
toks := strings.Split(p, "=")
|
||||
if len(toks) != 2 {
|
||||
trimmed := strings.TrimSpace(p)
|
||||
if len(trimmed) > 0 {
|
||||
terms = append(terms, trimmed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(toks[0])
|
||||
if key != model.EscapingKey {
|
||||
terms = append(terms, strings.TrimSpace(p))
|
||||
}
|
||||
}
|
||||
terms = append(terms, model.EscapingKey+"="+s.String())
|
||||
return Format(strings.Join(terms, "; "))
|
||||
}
|
||||
|
||||
// FormatType deduces an overall FormatType for the given format.
|
||||
func (f Format) FormatType() FormatType {
|
||||
toks := strings.Split(string(f), ";")
|
||||
params := make(map[string]string)
|
||||
for i, t := range toks {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
args := strings.Split(t, "=")
|
||||
if len(args) != 2 {
|
||||
continue
|
||||
}
|
||||
params[strings.TrimSpace(args[0])] = strings.TrimSpace(args[1])
|
||||
}
|
||||
|
||||
switch strings.TrimSpace(toks[0]) {
|
||||
case ProtoType:
|
||||
if params["proto"] != ProtoProtocol {
|
||||
return TypeUnknown
|
||||
}
|
||||
switch params["encoding"] {
|
||||
case "delimited":
|
||||
return TypeProtoDelim
|
||||
case "text":
|
||||
return TypeProtoText
|
||||
case "compact-text":
|
||||
return TypeProtoCompact
|
||||
default:
|
||||
return TypeUnknown
|
||||
}
|
||||
case OpenMetricsType:
|
||||
if params["charset"] != "utf-8" {
|
||||
return TypeUnknown
|
||||
}
|
||||
return TypeOpenMetrics
|
||||
case "text/plain":
|
||||
v, ok := params["version"]
|
||||
if !ok {
|
||||
return TypeTextPlain
|
||||
}
|
||||
if v == TextVersion {
|
||||
return TypeTextPlain
|
||||
}
|
||||
return TypeUnknown
|
||||
default:
|
||||
return TypeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
// ToEscapingScheme returns an EscapingScheme depending on the Format. Iff the
|
||||
// Format contains a escaping=allow-utf-8 term, it will select NoEscaping. If a valid
|
||||
// "escaping" term exists, that will be used. Otherwise, the global default will
|
||||
// be returned.
|
||||
func (f Format) ToEscapingScheme() model.EscapingScheme {
|
||||
for _, p := range strings.Split(string(f), ";") {
|
||||
toks := strings.Split(p, "=")
|
||||
if len(toks) != 2 {
|
||||
continue
|
||||
}
|
||||
key, value := strings.TrimSpace(toks[0]), strings.TrimSpace(toks[1])
|
||||
if key == model.EscapingKey {
|
||||
scheme, err := model.ToEscapingScheme(value)
|
||||
if err != nil {
|
||||
return model.NameEscapingScheme
|
||||
}
|
||||
return scheme
|
||||
}
|
||||
}
|
||||
return model.NameEscapingScheme
|
||||
}
|
||||
|
||||
10
vendor/github.com/prometheus/common/expfmt/fuzz.go
generated
vendored
10
vendor/github.com/prometheus/common/expfmt/fuzz.go
generated
vendored
@@ -13,11 +13,14 @@
|
||||
|
||||
// Build only when actually fuzzing
|
||||
//go:build gofuzz
|
||||
// +build gofuzz
|
||||
|
||||
package expfmt
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
|
||||
//
|
||||
@@ -26,9 +29,8 @@ import "bytes"
|
||||
//
|
||||
// Further input samples should go in the folder fuzz/corpus.
|
||||
func Fuzz(in []byte) int {
|
||||
parser := TextParser{}
|
||||
parser := NewTextParser(model.UTF8Validation)
|
||||
_, err := parser.TextToMetricFamilies(bytes.NewReader(in))
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
339
vendor/github.com/prometheus/common/expfmt/openmetrics_create.go
generated
vendored
339
vendor/github.com/prometheus/common/expfmt/openmetrics_create.go
generated
vendored
@@ -22,11 +22,46 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type encoderOption struct {
|
||||
withCreatedLines bool
|
||||
withUnit bool
|
||||
}
|
||||
|
||||
type EncoderOption func(*encoderOption)
|
||||
|
||||
// WithCreatedLines is an EncoderOption that configures the OpenMetrics encoder
|
||||
// to include _created lines (See
|
||||
// https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#counter-1).
|
||||
// Created timestamps can improve the accuracy of series reset detection, but
|
||||
// come with a bandwidth cost.
|
||||
//
|
||||
// At the time of writing, created timestamp ingestion is still experimental in
|
||||
// Prometheus and need to be enabled with the feature-flag
|
||||
// `--feature-flag=created-timestamp-zero-ingestion`, and breaking changes are
|
||||
// still possible. Therefore, it is recommended to use this feature with caution.
|
||||
func WithCreatedLines() EncoderOption {
|
||||
return func(t *encoderOption) {
|
||||
t.withCreatedLines = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithUnit is an EncoderOption enabling a set unit to be written to the output
|
||||
// and to be added to the metric name, if it's not there already, as a suffix.
|
||||
// Without opting in this way, the unit will not be added to the metric name and,
|
||||
// on top of that, the unit will not be passed onto the output, even if it
|
||||
// were declared in the *dto.MetricFamily struct, i.e. even if in.Unit !=nil.
|
||||
func WithUnit() EncoderOption {
|
||||
return func(t *encoderOption) {
|
||||
t.withUnit = true
|
||||
}
|
||||
}
|
||||
|
||||
// MetricFamilyToOpenMetrics converts a MetricFamily proto message into the
|
||||
// OpenMetrics text format and writes the resulting lines to 'out'. It returns
|
||||
// the number of bytes written and any error encountered. The output will have
|
||||
@@ -35,6 +70,18 @@ import (
|
||||
// sanity checks. If the input contains duplicate metrics or invalid metric or
|
||||
// label names, the conversion will result in invalid text format output.
|
||||
//
|
||||
// If metric names conform to the legacy validation pattern, they will be placed
|
||||
// outside the brackets in the traditional way, like `foo{}`. If the metric name
|
||||
// fails the legacy validation check, it will be placed quoted inside the
|
||||
// brackets: `{"foo"}`. As stated above, the input is assumed to be santized and
|
||||
// no error will be thrown in this case.
|
||||
//
|
||||
// Similar to metric names, if label names conform to the legacy validation
|
||||
// pattern, they will be unquoted as normal, like `foo{bar="baz"}`. If the label
|
||||
// name fails the legacy validation check, it will be quoted:
|
||||
// `foo{"bar"="baz"}`. As stated above, the input is assumed to be santized and
|
||||
// no error will be thrown in this case.
|
||||
//
|
||||
// This function fulfills the type 'expfmt.encoder'.
|
||||
//
|
||||
// Note that OpenMetrics requires a final `# EOF` line. Since this function acts
|
||||
@@ -47,20 +94,34 @@ import (
|
||||
// Prometheus to OpenMetrics or vice versa:
|
||||
//
|
||||
// - Counters are expected to have the `_total` suffix in their metric name. In
|
||||
// the output, the suffix will be truncated from the `# TYPE` and `# HELP`
|
||||
// line. A counter with a missing `_total` suffix is not an error. However,
|
||||
// the output, the suffix will be truncated from the `# TYPE`, `# HELP` and `# UNIT`
|
||||
// lines. A counter with a missing `_total` suffix is not an error. However,
|
||||
// its type will be set to `unknown` in that case to avoid invalid OpenMetrics
|
||||
// output.
|
||||
//
|
||||
// - No support for the following (optional) features: `# UNIT` line, `_created`
|
||||
// line, info type, stateset type, gaugehistogram type.
|
||||
// - According to the OM specs, the `# UNIT` line is optional, but if populated,
|
||||
// the unit has to be present in the metric name as its suffix:
|
||||
// (see https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#unit).
|
||||
// However, in order to accommodate any potential scenario where such a change in the
|
||||
// metric name is not desirable, the users are here given the choice of either explicitly
|
||||
// opt in, in case they wish for the unit to be included in the output AND in the metric name
|
||||
// as a suffix (see the description of the WithUnit function above),
|
||||
// or not to opt in, in case they don't want for any of that to happen.
|
||||
//
|
||||
// - No support for the following (optional) features: info type,
|
||||
// stateset type, gaugehistogram type.
|
||||
//
|
||||
// - The size of exemplar labels is not checked (i.e. it's possible to create
|
||||
// exemplars that are larger than allowed by the OpenMetrics specification).
|
||||
//
|
||||
// - The value of Counters is not checked. (OpenMetrics doesn't allow counters
|
||||
// with a `NaN` value.)
|
||||
func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int, err error) {
|
||||
func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...EncoderOption) (written int, err error) {
|
||||
toOM := encoderOption{}
|
||||
for _, option := range options {
|
||||
option(&toOM)
|
||||
}
|
||||
|
||||
name := in.GetName()
|
||||
if name == "" {
|
||||
return 0, fmt.Errorf("MetricFamily has no name: %s", in)
|
||||
@@ -83,12 +144,15 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int
|
||||
}
|
||||
|
||||
var (
|
||||
n int
|
||||
metricType = in.GetType()
|
||||
shortName = name
|
||||
n int
|
||||
metricType = in.GetType()
|
||||
compliantName = name
|
||||
)
|
||||
if metricType == dto.MetricType_COUNTER && strings.HasSuffix(shortName, "_total") {
|
||||
shortName = name[:len(name)-6]
|
||||
if metricType == dto.MetricType_COUNTER && strings.HasSuffix(compliantName, "_total") {
|
||||
compliantName = name[:len(name)-6]
|
||||
}
|
||||
if toOM.withUnit && in.Unit != nil && !strings.HasSuffix(compliantName, "_"+*in.Unit) {
|
||||
compliantName = compliantName + "_" + *in.Unit
|
||||
}
|
||||
|
||||
// Comments, first HELP, then TYPE.
|
||||
@@ -96,38 +160,38 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int
|
||||
n, err = w.WriteString("# HELP ")
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = w.WriteString(shortName)
|
||||
n, err = writeName(w, compliantName)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
err = w.WriteByte(' ')
|
||||
written++
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = writeEscapedString(w, *in.Help, true)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
err = w.WriteByte('\n')
|
||||
written++
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = w.WriteString("# TYPE ")
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = w.WriteString(shortName)
|
||||
n, err = writeName(w, compliantName)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
switch metricType {
|
||||
case dto.MetricType_COUNTER:
|
||||
@@ -144,102 +208,148 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int
|
||||
n, err = w.WriteString(" unknown\n")
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
n, err = w.WriteString(" histogram\n")
|
||||
case dto.MetricType_GAUGE_HISTOGRAM:
|
||||
n, err = w.WriteString(" gaugehistogram\n")
|
||||
default:
|
||||
return written, fmt.Errorf("unknown metric type %s", metricType.String())
|
||||
}
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
if toOM.withUnit && in.Unit != nil {
|
||||
n, err = w.WriteString("# UNIT ")
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
n, err = writeName(w, compliantName)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
|
||||
err = w.WriteByte(' ')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
n, err = writeEscapedString(w, *in.Unit, true)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
err = w.WriteByte('\n')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
|
||||
var createdTsBytesWritten int
|
||||
|
||||
// Finally the samples, one line for each.
|
||||
if metricType == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") {
|
||||
compliantName += "_total"
|
||||
}
|
||||
for _, metric := range in.Metric {
|
||||
switch metricType {
|
||||
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 %s", compliantName, metric,
|
||||
)
|
||||
}
|
||||
// Note that we have ensured above that either the name
|
||||
// ends on `_total` or that the rendered type is
|
||||
// `unknown`. Therefore, no `_total` must be added here.
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "", metric, "", 0,
|
||||
w, compliantName, "", metric, "", 0,
|
||||
metric.Counter.GetValue(), 0, false,
|
||||
metric.Counter.Exemplar,
|
||||
)
|
||||
if toOM.withCreatedLines && metric.Counter.CreatedTimestamp != nil {
|
||||
createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "_total", metric, "", 0, metric.Counter.GetCreatedTimestamp())
|
||||
n += createdTsBytesWritten
|
||||
}
|
||||
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 %s", compliantName, metric,
|
||||
)
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "", metric, "", 0,
|
||||
w, compliantName, "", metric, "", 0,
|
||||
metric.Gauge.GetValue(), 0, false,
|
||||
nil,
|
||||
)
|
||||
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 %s", compliantName, metric,
|
||||
)
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "", metric, "", 0,
|
||||
w, compliantName, "", metric, "", 0,
|
||||
metric.Untyped.GetValue(), 0, false,
|
||||
nil,
|
||||
)
|
||||
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 %s", compliantName, metric,
|
||||
)
|
||||
}
|
||||
for _, q := range metric.Summary.Quantile {
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "", metric,
|
||||
w, compliantName, "", metric,
|
||||
model.QuantileLabel, q.GetQuantile(),
|
||||
q.GetValue(), 0, false,
|
||||
nil,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "_sum", metric, "", 0,
|
||||
w, compliantName, "_sum", metric, "", 0,
|
||||
metric.Summary.GetSampleSum(), 0, false,
|
||||
nil,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "_count", metric, "", 0,
|
||||
w, compliantName, "_count", metric, "", 0,
|
||||
0, metric.Summary.GetSampleCount(), true,
|
||||
nil,
|
||||
)
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
if toOM.withCreatedLines && metric.Summary.CreatedTimestamp != nil {
|
||||
createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "", metric, "", 0, metric.Summary.GetCreatedTimestamp())
|
||||
n += createdTsBytesWritten
|
||||
}
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
if metric.Histogram == nil {
|
||||
return written, fmt.Errorf(
|
||||
"expected histogram in metric %s %s", name, metric,
|
||||
"expected histogram in metric %s %s", compliantName, metric,
|
||||
)
|
||||
}
|
||||
infSeen := false
|
||||
for _, b := range metric.Histogram.Bucket {
|
||||
if b.GetCumulativeCountFloat() > 0 {
|
||||
return written, fmt.Errorf(
|
||||
"OpenMetrics v1.0 does not support float histogram %s %s",
|
||||
compliantName, metric,
|
||||
)
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "_bucket", metric,
|
||||
w, compliantName, "_bucket", metric,
|
||||
model.BucketLabel, b.GetUpperBound(),
|
||||
0, b.GetCumulativeCount(), true,
|
||||
b.Exemplar,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
if math.IsInf(b.GetUpperBound(), +1) {
|
||||
infSeen = true
|
||||
@@ -247,41 +357,54 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily) (written int
|
||||
}
|
||||
if !infSeen {
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "_bucket", metric,
|
||||
w, compliantName, "_bucket", metric,
|
||||
model.BucketLabel, math.Inf(+1),
|
||||
0, metric.Histogram.GetSampleCount(), true,
|
||||
nil,
|
||||
)
|
||||
// We do not check for a float sample count here
|
||||
// because we will check for it below (and error
|
||||
// out if needed).
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "_sum", metric, "", 0,
|
||||
w, compliantName, "_sum", metric, "", 0,
|
||||
metric.Histogram.GetSampleSum(), 0, false,
|
||||
nil,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
if metric.Histogram.GetSampleCountFloat() > 0 {
|
||||
return written, fmt.Errorf(
|
||||
"OpenMetrics v1.0 does not support float histogram %s %s",
|
||||
compliantName, metric,
|
||||
)
|
||||
}
|
||||
n, err = writeOpenMetricsSample(
|
||||
w, name, "_count", metric, "", 0,
|
||||
w, compliantName, "_count", metric, "", 0,
|
||||
0, metric.Histogram.GetSampleCount(), true,
|
||||
nil,
|
||||
)
|
||||
if toOM.withCreatedLines && metric.Histogram.CreatedTimestamp != nil {
|
||||
createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "", metric, "", 0, metric.Histogram.GetCreatedTimestamp())
|
||||
n += createdTsBytesWritten
|
||||
}
|
||||
default:
|
||||
return written, fmt.Errorf(
|
||||
"unexpected type in metric %s %s", name, metric,
|
||||
"unexpected type in metric %s %s", compliantName, metric,
|
||||
)
|
||||
}
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
|
||||
// FinalizeOpenMetrics writes the final `# EOF\n` line required by OpenMetrics.
|
||||
@@ -303,21 +426,9 @@ func writeOpenMetricsSample(
|
||||
floatValue float64, intValue uint64, useIntValue bool,
|
||||
exemplar *dto.Exemplar,
|
||||
) (int, error) {
|
||||
var written int
|
||||
n, err := w.WriteString(name)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
if suffix != "" {
|
||||
n, err = w.WriteString(suffix)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = writeOpenMetricsLabelPairs(
|
||||
w, metric.Label, additionalLabelName, additionalLabelValue,
|
||||
written := 0
|
||||
n, err := writeOpenMetricsNameAndLabelPairs(
|
||||
w, name+suffix, metric.Label, additionalLabelName, additionalLabelValue,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
@@ -350,7 +461,7 @@ func writeOpenMetricsSample(
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
if exemplar != nil {
|
||||
if exemplar != nil && len(exemplar.Label) > 0 {
|
||||
n, err = writeExemplar(w, exemplar)
|
||||
written += n
|
||||
if err != nil {
|
||||
@@ -365,27 +476,58 @@ func writeOpenMetricsSample(
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// writeOpenMetricsLabelPairs works like writeOpenMetrics but formats the float
|
||||
// in OpenMetrics style.
|
||||
func writeOpenMetricsLabelPairs(
|
||||
// writeOpenMetricsNameAndLabelPairs works like writeOpenMetricsSample but
|
||||
// formats the float in OpenMetrics style.
|
||||
func writeOpenMetricsNameAndLabelPairs(
|
||||
w enhancedWriter,
|
||||
name string,
|
||||
in []*dto.LabelPair,
|
||||
additionalLabelName string, additionalLabelValue float64,
|
||||
) (int, error) {
|
||||
if len(in) == 0 && additionalLabelName == "" {
|
||||
return 0, nil
|
||||
}
|
||||
var (
|
||||
written int
|
||||
separator byte = '{'
|
||||
written int
|
||||
separator byte = '{'
|
||||
metricInsideBraces = false
|
||||
)
|
||||
|
||||
if name != "" {
|
||||
// If the name does not pass the legacy validity check, we must put the
|
||||
// metric name inside the braces, quoted.
|
||||
if !model.LegacyValidation.IsValidMetricName(name) {
|
||||
metricInsideBraces = true
|
||||
err := w.WriteByte(separator)
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
separator = ','
|
||||
}
|
||||
|
||||
n, err := writeName(w, name)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(in) == 0 && additionalLabelName == "" {
|
||||
if metricInsideBraces {
|
||||
err := w.WriteByte('}')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
for _, lp := range in {
|
||||
err := w.WriteByte(separator)
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
n, err := w.WriteString(lp.GetName())
|
||||
n, err := writeName(w, lp.GetName())
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
@@ -442,6 +584,49 @@ func writeOpenMetricsLabelPairs(
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// writeOpenMetricsCreated writes the created timestamp for a single time series
|
||||
// following OpenMetrics text format to w, given the metric name, the metric proto
|
||||
// message itself, optionally a suffix to be removed, e.g. '_total' for counters,
|
||||
// an additional label name with a float64 value (use empty string as label name if
|
||||
// not required) and the timestamp that represents the created timestamp.
|
||||
// The function returns the number of bytes written and any error encountered.
|
||||
func writeOpenMetricsCreated(w enhancedWriter,
|
||||
name, suffixToTrim string, metric *dto.Metric,
|
||||
additionalLabelName string, additionalLabelValue float64,
|
||||
createdTimestamp *timestamppb.Timestamp,
|
||||
) (int, error) {
|
||||
written := 0
|
||||
n, err := writeOpenMetricsNameAndLabelPairs(
|
||||
w, strings.TrimSuffix(name, suffixToTrim)+"_created", metric.Label, additionalLabelName, additionalLabelValue,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
|
||||
err = w.WriteByte(' ')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
|
||||
// TODO(beorn7): Format this directly from components of ts to
|
||||
// avoid overflow/underflow and precision issues of the float
|
||||
// conversion.
|
||||
n, err = writeOpenMetricsFloat(w, float64(createdTimestamp.AsTime().UnixNano())/1e9)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
|
||||
err = w.WriteByte('\n')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// writeExemplar writes the provided exemplar in OpenMetrics format to w. The
|
||||
// function returns the number of bytes written and any error encountered.
|
||||
func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) {
|
||||
@@ -451,7 +636,7 @@ func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) {
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
n, err = writeOpenMetricsLabelPairs(w, e.Label, "", 0)
|
||||
n, err = writeOpenMetricsNameAndLabelPairs(w, "", e.Label, "", 0)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
@@ -472,11 +657,11 @@ func writeExemplar(w enhancedWriter, e *dto.Exemplar) (int, error) {
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
err = (*e).Timestamp.CheckValid()
|
||||
err = e.Timestamp.CheckValid()
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
ts := (*e).Timestamp.AsTime()
|
||||
ts := e.Timestamp.AsTime()
|
||||
// TODO(beorn7): Format this directly from components of ts to
|
||||
// avoid overflow/underflow and precision issues of the float
|
||||
// conversion.
|
||||
|
||||
180
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
180
vendor/github.com/prometheus/common/expfmt/text_create.go
generated
vendored
@@ -22,9 +22,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
// enhancedWriter has all the enhanced write functions needed here. bufio.Writer
|
||||
@@ -62,6 +62,18 @@ var (
|
||||
// contains duplicate metrics or invalid metric or label names, the conversion
|
||||
// will result in invalid text format output.
|
||||
//
|
||||
// If metric names conform to the legacy validation pattern, they will be placed
|
||||
// outside the brackets in the traditional way, like `foo{}`. If the metric name
|
||||
// fails the legacy validation check, it will be placed quoted inside the
|
||||
// brackets: `{"foo"}`. As stated above, the input is assumed to be santized and
|
||||
// no error will be thrown in this case.
|
||||
//
|
||||
// Similar to metric names, if label names conform to the legacy validation
|
||||
// pattern, they will be unquoted as normal, like `foo{bar="baz"}`. If the label
|
||||
// name fails the legacy validation check, it will be quoted:
|
||||
// `foo{"bar"="baz"}`. As stated above, the input is assumed to be santized and
|
||||
// no error will be thrown in this case.
|
||||
//
|
||||
// This method fulfills the type 'prometheus.encoder'.
|
||||
func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) {
|
||||
// Fail-fast checks.
|
||||
@@ -96,38 +108,38 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
n, err = w.WriteString("# HELP ")
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = w.WriteString(name)
|
||||
n, err = writeName(w, name)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
err = w.WriteByte(' ')
|
||||
written++
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = writeEscapedString(w, *in.Help, false)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
err = w.WriteByte('\n')
|
||||
written++
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = w.WriteString("# TYPE ")
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = w.WriteString(name)
|
||||
n, err = writeName(w, name)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
metricType := in.GetType()
|
||||
switch metricType {
|
||||
@@ -139,14 +151,17 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
n, err = w.WriteString(" summary\n")
|
||||
case dto.MetricType_UNTYPED:
|
||||
n, err = w.WriteString(" untyped\n")
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
// The classic Prometheus text format has no notion of a gauge
|
||||
// histogram. We render a gauge histogram in the same way as a
|
||||
// regular histogram.
|
||||
n, err = w.WriteString(" histogram\n")
|
||||
default:
|
||||
return written, fmt.Errorf("unknown metric type %s", metricType.String())
|
||||
}
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
|
||||
// Finally the samples, one line for each.
|
||||
@@ -196,7 +211,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = writeSample(
|
||||
@@ -205,13 +220,13 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = writeSample(
|
||||
w, name, "_count", metric, "", 0,
|
||||
float64(metric.Summary.GetSampleCount()),
|
||||
)
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
if metric.Histogram == nil {
|
||||
return written, fmt.Errorf(
|
||||
"expected histogram in metric %s %s", name, metric,
|
||||
@@ -219,28 +234,36 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
}
|
||||
infSeen := false
|
||||
for _, b := range metric.Histogram.Bucket {
|
||||
v := b.GetCumulativeCountFloat()
|
||||
if v == 0 {
|
||||
v = float64(b.GetCumulativeCount())
|
||||
}
|
||||
n, err = writeSample(
|
||||
w, name, "_bucket", metric,
|
||||
model.BucketLabel, b.GetUpperBound(),
|
||||
float64(b.GetCumulativeCount()),
|
||||
v,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
if math.IsInf(b.GetUpperBound(), +1) {
|
||||
infSeen = true
|
||||
}
|
||||
}
|
||||
if !infSeen {
|
||||
v := metric.Histogram.GetSampleCountFloat()
|
||||
if v == 0 {
|
||||
v = float64(metric.Histogram.GetSampleCount())
|
||||
}
|
||||
n, err = writeSample(
|
||||
w, name, "_bucket", metric,
|
||||
model.BucketLabel, math.Inf(+1),
|
||||
float64(metric.Histogram.GetSampleCount()),
|
||||
v,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = writeSample(
|
||||
@@ -249,12 +272,13 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
n, err = writeSample(
|
||||
w, name, "_count", metric, "", 0,
|
||||
float64(metric.Histogram.GetSampleCount()),
|
||||
)
|
||||
v := metric.Histogram.GetSampleCountFloat()
|
||||
if v == 0 {
|
||||
v = float64(metric.Histogram.GetSampleCount())
|
||||
}
|
||||
n, err = writeSample(w, name, "_count", metric, "", 0, v)
|
||||
default:
|
||||
return written, fmt.Errorf(
|
||||
"unexpected type in metric %s %s", name, metric,
|
||||
@@ -262,10 +286,10 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
|
||||
}
|
||||
written += n
|
||||
if err != nil {
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
return
|
||||
return written, err
|
||||
}
|
||||
|
||||
// writeSample writes a single sample in text format to w, given the metric
|
||||
@@ -280,21 +304,9 @@ func writeSample(
|
||||
additionalLabelName string, additionalLabelValue float64,
|
||||
value float64,
|
||||
) (int, error) {
|
||||
var written int
|
||||
n, err := w.WriteString(name)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
if suffix != "" {
|
||||
n, err = w.WriteString(suffix)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
n, err = writeLabelPairs(
|
||||
w, metric.Label, additionalLabelName, additionalLabelValue,
|
||||
written := 0
|
||||
n, err := writeNameAndLabelPairs(
|
||||
w, name+suffix, metric.Label, additionalLabelName, additionalLabelValue,
|
||||
)
|
||||
written += n
|
||||
if err != nil {
|
||||
@@ -330,32 +342,64 @@ func writeSample(
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// writeLabelPairs converts a slice of LabelPair proto messages plus the
|
||||
// explicitly given additional label pair into text formatted as required by the
|
||||
// text format and writes it to 'w'. An empty slice in combination with an empty
|
||||
// string 'additionalLabelName' results in nothing being written. Otherwise, the
|
||||
// label pairs are written, escaped as required by the text format, and enclosed
|
||||
// in '{...}'. The function returns the number of bytes written and any error
|
||||
// encountered.
|
||||
func writeLabelPairs(
|
||||
// writeNameAndLabelPairs converts a slice of LabelPair proto messages plus the
|
||||
// explicitly given metric name and additional label pair into text formatted as
|
||||
// required by the text format and writes it to 'w'. An empty slice in
|
||||
// combination with an empty string 'additionalLabelName' results in nothing
|
||||
// being written. Otherwise, the label pairs are written, escaped as required by
|
||||
// the text format, and enclosed in '{...}'. The function returns the number of
|
||||
// bytes written and any error encountered. If the metric name is not
|
||||
// legacy-valid, it will be put inside the brackets as well. Legacy-invalid
|
||||
// label names will also be quoted.
|
||||
func writeNameAndLabelPairs(
|
||||
w enhancedWriter,
|
||||
name string,
|
||||
in []*dto.LabelPair,
|
||||
additionalLabelName string, additionalLabelValue float64,
|
||||
) (int, error) {
|
||||
if len(in) == 0 && additionalLabelName == "" {
|
||||
return 0, nil
|
||||
}
|
||||
var (
|
||||
written int
|
||||
separator byte = '{'
|
||||
written int
|
||||
separator byte = '{'
|
||||
metricInsideBraces = false
|
||||
)
|
||||
|
||||
if name != "" {
|
||||
// If the name does not pass the legacy validity check, we must put the
|
||||
// metric name inside the braces.
|
||||
if !model.LegacyValidation.IsValidMetricName(name) {
|
||||
metricInsideBraces = true
|
||||
err := w.WriteByte(separator)
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
separator = ','
|
||||
}
|
||||
n, err := writeName(w, name)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(in) == 0 && additionalLabelName == "" {
|
||||
if metricInsideBraces {
|
||||
err := w.WriteByte('}')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
for _, lp := range in {
|
||||
err := w.WriteByte(separator)
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
n, err := w.WriteString(lp.GetName())
|
||||
n, err := writeName(w, lp.GetName())
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
@@ -462,3 +506,27 @@ func writeInt(w enhancedWriter, i int64) (int, error) {
|
||||
numBufPool.Put(bp)
|
||||
return written, err
|
||||
}
|
||||
|
||||
// writeName writes a string as-is if it complies with the legacy naming
|
||||
// scheme, or escapes it in double quotes if not.
|
||||
func writeName(w enhancedWriter, name string) (int, error) {
|
||||
if model.LegacyValidation.IsValidMetricName(name) {
|
||||
return w.WriteString(name)
|
||||
}
|
||||
var written int
|
||||
var err error
|
||||
err = w.WriteByte('"')
|
||||
written++
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
var n int
|
||||
n, err = writeEscapedString(w, name, true)
|
||||
written += n
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
err = w.WriteByte('"')
|
||||
written++
|
||||
return written, err
|
||||
}
|
||||
|
||||
318
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
318
vendor/github.com/prometheus/common/expfmt/text_parse.go
generated
vendored
@@ -16,15 +16,17 @@ package expfmt
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"github.com/golang/protobuf/proto" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
@@ -46,8 +48,10 @@ 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
|
||||
// zero value is ready to use.
|
||||
// TextParser is used to parse the simple and flat text-based exchange format.
|
||||
//
|
||||
// TextParser instances must be created with NewTextParser, the zero value of
|
||||
// TextParser is invalid.
|
||||
type TextParser struct {
|
||||
metricFamiliesByName map[string]*dto.MetricFamily
|
||||
buf *bufio.Reader // Where the parsed input is read through.
|
||||
@@ -58,6 +62,7 @@ type TextParser struct {
|
||||
currentMF *dto.MetricFamily
|
||||
currentMetric *dto.Metric
|
||||
currentLabelPair *dto.LabelPair
|
||||
currentLabelPairs []*dto.LabelPair // Temporarily stores label pairs while parsing a metric line.
|
||||
|
||||
// The remaining member variables are only used for summaries/histograms.
|
||||
currentLabels map[string]string // All labels including '__name__' but excluding 'quantile'/'le'
|
||||
@@ -72,6 +77,17 @@ type TextParser struct {
|
||||
// count and sum of that summary/histogram.
|
||||
currentIsSummaryCount, currentIsSummarySum bool
|
||||
currentIsHistogramCount, currentIsHistogramSum bool
|
||||
// These indicate if the metric name from the current line being parsed is inside
|
||||
// braces and if that metric name was found respectively.
|
||||
currentMetricIsInsideBraces, currentMetricInsideBracesIsPresent bool
|
||||
// scheme sets the desired ValidationScheme for names. Defaults to the invalid
|
||||
// UnsetValidation.
|
||||
scheme model.ValidationScheme
|
||||
}
|
||||
|
||||
// NewTextParser returns a new TextParser with the provided nameValidationScheme.
|
||||
func NewTextParser(nameValidationScheme model.ValidationScheme) TextParser {
|
||||
return TextParser{scheme: nameValidationScheme}
|
||||
}
|
||||
|
||||
// TextToMetricFamilies reads 'in' as the simple and flat text-based exchange
|
||||
@@ -112,14 +128,50 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF
|
||||
// stream. Turn this error into something nicer and more
|
||||
// meaningful. (io.EOF is often used as a signal for the legitimate end
|
||||
// of an input stream.)
|
||||
if p.err == io.EOF {
|
||||
if p.err != nil && errors.Is(p.err, io.EOF) {
|
||||
p.parseError("unexpected end of input stream")
|
||||
}
|
||||
for _, histogramMetric := range p.histograms {
|
||||
normalizeHistogram(histogramMetric.GetHistogram())
|
||||
}
|
||||
return p.metricFamiliesByName, p.err
|
||||
}
|
||||
|
||||
// normalizeHistogram makes sure that all the buckets and the count in each
|
||||
// histogram is either completely float or completely integer.
|
||||
func normalizeHistogram(histogram *dto.Histogram) {
|
||||
if histogram == nil {
|
||||
return
|
||||
}
|
||||
anyFloats := false
|
||||
if histogram.GetSampleCountFloat() != 0 {
|
||||
anyFloats = true
|
||||
} else {
|
||||
for _, b := range histogram.GetBucket() {
|
||||
if b.GetCumulativeCountFloat() != 0 {
|
||||
anyFloats = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !anyFloats {
|
||||
return
|
||||
}
|
||||
if histogram.GetSampleCountFloat() == 0 {
|
||||
histogram.SampleCountFloat = proto.Float64(float64(histogram.GetSampleCount()))
|
||||
histogram.SampleCount = nil
|
||||
}
|
||||
for _, b := range histogram.GetBucket() {
|
||||
if b.GetCumulativeCountFloat() == 0 {
|
||||
b.CumulativeCountFloat = proto.Float64(float64(b.GetCumulativeCount()))
|
||||
b.CumulativeCount = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *TextParser) reset(in io.Reader) {
|
||||
p.metricFamiliesByName = map[string]*dto.MetricFamily{}
|
||||
p.currentLabelPairs = nil
|
||||
if p.buf == nil {
|
||||
p.buf = bufio.NewReader(in)
|
||||
} else {
|
||||
@@ -135,18 +187,21 @@ func (p *TextParser) reset(in io.Reader) {
|
||||
}
|
||||
p.currentQuantile = math.NaN()
|
||||
p.currentBucket = math.NaN()
|
||||
p.currentMF = nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
p.lineCount++
|
||||
p.currentMetricIsInsideBraces = false
|
||||
p.currentMetricInsideBracesIsPresent = false
|
||||
if p.skipBlankTab(); p.err != nil {
|
||||
// This is the only place that we expect to see io.EOF,
|
||||
// which is not an error but the signal that we are done.
|
||||
// Any other error that happens to align with the start of
|
||||
// a line is still an error.
|
||||
if p.err == io.EOF {
|
||||
if errors.Is(p.err, io.EOF) {
|
||||
p.err = nil
|
||||
}
|
||||
return nil
|
||||
@@ -156,6 +211,9 @@ func (p *TextParser) startOfLine() stateFn {
|
||||
return p.startComment
|
||||
case '\n':
|
||||
return p.startOfLine // Empty line, start the next one.
|
||||
case '{':
|
||||
p.currentMetricIsInsideBraces = true
|
||||
return p.readingLabels
|
||||
}
|
||||
return p.readingMetricName
|
||||
}
|
||||
@@ -204,6 +262,9 @@ func (p *TextParser) startComment() stateFn {
|
||||
return nil
|
||||
}
|
||||
p.setOrCreateCurrentMF()
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
if p.skipBlankTab(); p.err != nil {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
@@ -232,6 +293,9 @@ func (p *TextParser) readingMetricName() stateFn {
|
||||
return nil
|
||||
}
|
||||
p.setOrCreateCurrentMF()
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
// Now is the time to fix the type if it hasn't happened yet.
|
||||
if p.currentMF.Type == nil {
|
||||
p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
|
||||
@@ -254,7 +318,9 @@ func (p *TextParser) readingLabels() stateFn {
|
||||
// Summaries/histograms are special. We have to reset the
|
||||
// currentLabels map, currentQuantile and currentBucket before starting to
|
||||
// read labels.
|
||||
if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||
if p.currentMF.GetType() == dto.MetricType_SUMMARY ||
|
||||
p.currentMF.GetType() == dto.MetricType_HISTOGRAM ||
|
||||
p.currentMF.GetType() == dto.MetricType_GAUGE_HISTOGRAM {
|
||||
p.currentLabels = map[string]string{}
|
||||
p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName()
|
||||
p.currentQuantile = math.NaN()
|
||||
@@ -273,6 +339,8 @@ func (p *TextParser) startLabelName() stateFn {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
if p.currentByte == '}' {
|
||||
p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPairs...)
|
||||
p.currentLabelPairs = nil
|
||||
if p.skipBlankTab(); p.err != nil {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
@@ -285,34 +353,81 @@ func (p *TextParser) startLabelName() stateFn {
|
||||
p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName()))
|
||||
return nil
|
||||
}
|
||||
p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
|
||||
if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
|
||||
p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
|
||||
return nil
|
||||
}
|
||||
// Special summary/histogram treatment. Don't add 'quantile' and 'le'
|
||||
// labels to 'real' labels.
|
||||
if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) &&
|
||||
!(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) {
|
||||
p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair)
|
||||
}
|
||||
if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
if p.currentByte != '=' {
|
||||
if p.currentMetricIsInsideBraces {
|
||||
if p.currentMetricInsideBracesIsPresent {
|
||||
p.parseError(fmt.Sprintf("multiple metric names for metric %q", p.currentMF.GetName()))
|
||||
return nil
|
||||
}
|
||||
switch p.currentByte {
|
||||
case ',':
|
||||
p.setOrCreateCurrentMF()
|
||||
if p.err != nil {
|
||||
return nil
|
||||
}
|
||||
if p.currentMF.Type == nil {
|
||||
p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
|
||||
}
|
||||
p.currentMetric = &dto.Metric{}
|
||||
p.currentMetricInsideBracesIsPresent = true
|
||||
return p.startLabelName
|
||||
case '}':
|
||||
p.setOrCreateCurrentMF()
|
||||
if p.err != nil {
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
if p.currentMF.Type == nil {
|
||||
p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
|
||||
}
|
||||
p.currentMetric = &dto.Metric{}
|
||||
p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPairs...)
|
||||
p.currentLabelPairs = nil
|
||||
if p.skipBlankTab(); p.err != nil {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
return p.readingValue
|
||||
default:
|
||||
p.parseError(fmt.Sprintf("unexpected end of metric name %q", p.currentByte))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
|
||||
if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
|
||||
p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
if !p.scheme.IsValidLabelName(p.currentLabelPair.GetName()) {
|
||||
p.parseError(fmt.Sprintf("invalid label name %q", p.currentLabelPair.GetName()))
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
// Special summary/histogram treatment. Don't add 'quantile' and 'le'
|
||||
// labels to 'real' labels.
|
||||
if (p.currentMF.GetType() != dto.MetricType_SUMMARY || p.currentLabelPair.GetName() != model.QuantileLabel) &&
|
||||
((p.currentMF.GetType() != dto.MetricType_HISTOGRAM &&
|
||||
p.currentMF.GetType() != dto.MetricType_GAUGE_HISTOGRAM) ||
|
||||
p.currentLabelPair.GetName() != model.BucketLabel) {
|
||||
p.currentLabelPairs = append(p.currentLabelPairs, p.currentLabelPair)
|
||||
}
|
||||
// Check for duplicate label names.
|
||||
labels := make(map[string]struct{})
|
||||
for _, l := range p.currentMetric.Label {
|
||||
for _, l := range p.currentLabelPairs {
|
||||
lName := l.GetName()
|
||||
if _, exists := labels[lName]; !exists {
|
||||
labels[lName] = struct{}{}
|
||||
} else {
|
||||
if _, exists := labels[lName]; exists {
|
||||
p.parseError(fmt.Sprintf("duplicate label names for metric %q", p.currentMF.GetName()))
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
labels[lName] = struct{}{}
|
||||
}
|
||||
return p.startLabelValue
|
||||
}
|
||||
@@ -343,6 +458,7 @@ func (p *TextParser) startLabelValue() stateFn {
|
||||
if p.currentQuantile, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
|
||||
// Create a more helpful error message.
|
||||
p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
@@ -350,7 +466,7 @@ func (p *TextParser) startLabelValue() stateFn {
|
||||
}
|
||||
}
|
||||
// Similar special treatment of histograms.
|
||||
if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||
if p.currentMF.GetType() == dto.MetricType_HISTOGRAM || p.currentMF.GetType() == dto.MetricType_GAUGE_HISTOGRAM {
|
||||
if p.currentLabelPair.GetName() == model.BucketLabel {
|
||||
if p.currentBucket, p.err = parseFloat(p.currentLabelPair.GetValue()); p.err != nil {
|
||||
// Create a more helpful error message.
|
||||
@@ -369,12 +485,19 @@ func (p *TextParser) startLabelValue() stateFn {
|
||||
return p.startLabelName
|
||||
|
||||
case '}':
|
||||
if p.currentMF == nil {
|
||||
p.parseError("invalid metric name")
|
||||
return nil
|
||||
}
|
||||
p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPairs...)
|
||||
p.currentLabelPairs = nil
|
||||
if p.skipBlankTab(); p.err != nil {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
return p.readingValue
|
||||
default:
|
||||
p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.GetValue()))
|
||||
p.currentLabelPairs = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -385,7 +508,8 @@ func (p *TextParser) readingValue() stateFn {
|
||||
// When we are here, we have read all the labels, so for the
|
||||
// special case of a summary/histogram, we can finally find out
|
||||
// if the metric already exists.
|
||||
if p.currentMF.GetType() == dto.MetricType_SUMMARY {
|
||||
switch p.currentMF.GetType() {
|
||||
case dto.MetricType_SUMMARY:
|
||||
signature := model.LabelsToSignature(p.currentLabels)
|
||||
if summary := p.summaries[signature]; summary != nil {
|
||||
p.currentMetric = summary
|
||||
@@ -393,7 +517,7 @@ func (p *TextParser) readingValue() stateFn {
|
||||
p.summaries[signature] = p.currentMetric
|
||||
p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
|
||||
}
|
||||
} else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
signature := model.LabelsToSignature(p.currentLabels)
|
||||
if histogram := p.histograms[signature]; histogram != nil {
|
||||
p.currentMetric = histogram
|
||||
@@ -401,7 +525,7 @@ func (p *TextParser) readingValue() stateFn {
|
||||
p.histograms[signature] = p.currentMetric
|
||||
p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
|
||||
}
|
||||
if p.readTokenUntilWhitespace(); p.err != nil {
|
||||
@@ -439,24 +563,38 @@ func (p *TextParser) readingValue() stateFn {
|
||||
},
|
||||
)
|
||||
}
|
||||
case dto.MetricType_HISTOGRAM:
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
// *sigh*
|
||||
if p.currentMetric.Histogram == nil {
|
||||
p.currentMetric.Histogram = &dto.Histogram{}
|
||||
}
|
||||
switch {
|
||||
case p.currentIsHistogramCount:
|
||||
p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value))
|
||||
if uintValue := uint64(value); value == float64(uintValue) {
|
||||
p.currentMetric.Histogram.SampleCount = proto.Uint64(uintValue)
|
||||
} else {
|
||||
if value < 0 {
|
||||
p.parseError(fmt.Sprintf("negative count for histogram %q", p.currentMF.GetName()))
|
||||
return nil
|
||||
}
|
||||
p.currentMetric.Histogram.SampleCountFloat = proto.Float64(value)
|
||||
}
|
||||
case p.currentIsHistogramSum:
|
||||
p.currentMetric.Histogram.SampleSum = proto.Float64(value)
|
||||
case !math.IsNaN(p.currentBucket):
|
||||
p.currentMetric.Histogram.Bucket = append(
|
||||
p.currentMetric.Histogram.Bucket,
|
||||
&dto.Bucket{
|
||||
UpperBound: proto.Float64(p.currentBucket),
|
||||
CumulativeCount: proto.Uint64(uint64(value)),
|
||||
},
|
||||
)
|
||||
b := &dto.Bucket{
|
||||
UpperBound: proto.Float64(p.currentBucket),
|
||||
}
|
||||
if uintValue := uint64(value); value == float64(uintValue) {
|
||||
b.CumulativeCount = proto.Uint64(uintValue)
|
||||
} else {
|
||||
if value < 0 {
|
||||
p.parseError(fmt.Sprintf("negative bucket population for histogram %q", p.currentMF.GetName()))
|
||||
return nil
|
||||
}
|
||||
b.CumulativeCountFloat = proto.Float64(value)
|
||||
}
|
||||
p.currentMetric.Histogram.Bucket = append(p.currentMetric.Histogram.Bucket, b)
|
||||
}
|
||||
default:
|
||||
p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName())
|
||||
@@ -519,10 +657,18 @@ func (p *TextParser) readingType() stateFn {
|
||||
if p.readTokenUntilNewline(false); p.err != nil {
|
||||
return nil // Unexpected end of input.
|
||||
}
|
||||
metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
|
||||
typ := strings.ToUpper(p.currentToken.String()) // Tolerate any combination of upper and lower case.
|
||||
metricType, ok := dto.MetricType_value[typ] // Tolerate "gauge_histogram" (not originally part of the text format).
|
||||
if !ok {
|
||||
p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
|
||||
return nil
|
||||
// We also want to tolerate "gaugehistogram" to mark a gauge
|
||||
// histogram, because that string is used in OpenMetrics. Note,
|
||||
// however, that gauge histograms do not officially exist in the
|
||||
// classic text format.
|
||||
if typ != "GAUGEHISTOGRAM" {
|
||||
p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
|
||||
return nil
|
||||
}
|
||||
metricType = int32(dto.MetricType_GAUGE_HISTOGRAM)
|
||||
}
|
||||
p.currentMF.Type = dto.MetricType(metricType).Enum()
|
||||
return p.startOfLine
|
||||
@@ -583,6 +729,8 @@ func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
case 'n':
|
||||
p.currentToken.WriteByte('\n')
|
||||
case '"':
|
||||
p.currentToken.WriteByte('"')
|
||||
default:
|
||||
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||
return
|
||||
@@ -608,13 +756,45 @@ func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
|
||||
// but not into p.currentToken.
|
||||
func (p *TextParser) readTokenAsMetricName() {
|
||||
p.currentToken.Reset()
|
||||
// A UTF-8 metric name must be quoted and may have escaped characters.
|
||||
quoted := false
|
||||
escaped := false
|
||||
if !isValidMetricNameStart(p.currentByte) {
|
||||
return
|
||||
}
|
||||
for {
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
for p.err == nil {
|
||||
if escaped {
|
||||
switch p.currentByte {
|
||||
case '\\':
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
case 'n':
|
||||
p.currentToken.WriteByte('\n')
|
||||
case '"':
|
||||
p.currentToken.WriteByte('"')
|
||||
default:
|
||||
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||
return
|
||||
}
|
||||
escaped = false
|
||||
} else {
|
||||
switch p.currentByte {
|
||||
case '"':
|
||||
quoted = !quoted
|
||||
if !quoted {
|
||||
p.currentByte, p.err = p.buf.ReadByte()
|
||||
return
|
||||
}
|
||||
case '\n':
|
||||
p.parseError(fmt.Sprintf("metric name %q contains unescaped new-line", p.currentToken.String()))
|
||||
return
|
||||
case '\\':
|
||||
escaped = true
|
||||
default:
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
}
|
||||
}
|
||||
p.currentByte, p.err = p.buf.ReadByte()
|
||||
if p.err != nil || !isValidMetricNameContinuation(p.currentByte) {
|
||||
if !isValidMetricNameContinuation(p.currentByte, quoted) || (!quoted && p.currentByte == ' ') {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -626,13 +806,45 @@ func (p *TextParser) readTokenAsMetricName() {
|
||||
// but not into p.currentToken.
|
||||
func (p *TextParser) readTokenAsLabelName() {
|
||||
p.currentToken.Reset()
|
||||
// A UTF-8 label name must be quoted and may have escaped characters.
|
||||
quoted := false
|
||||
escaped := false
|
||||
if !isValidLabelNameStart(p.currentByte) {
|
||||
return
|
||||
}
|
||||
for {
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
for p.err == nil {
|
||||
if escaped {
|
||||
switch p.currentByte {
|
||||
case '\\':
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
case 'n':
|
||||
p.currentToken.WriteByte('\n')
|
||||
case '"':
|
||||
p.currentToken.WriteByte('"')
|
||||
default:
|
||||
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||
return
|
||||
}
|
||||
escaped = false
|
||||
} else {
|
||||
switch p.currentByte {
|
||||
case '"':
|
||||
quoted = !quoted
|
||||
if !quoted {
|
||||
p.currentByte, p.err = p.buf.ReadByte()
|
||||
return
|
||||
}
|
||||
case '\n':
|
||||
p.parseError(fmt.Sprintf("label name %q contains unescaped new-line", p.currentToken.String()))
|
||||
return
|
||||
case '\\':
|
||||
escaped = true
|
||||
default:
|
||||
p.currentToken.WriteByte(p.currentByte)
|
||||
}
|
||||
}
|
||||
p.currentByte, p.err = p.buf.ReadByte()
|
||||
if p.err != nil || !isValidLabelNameContinuation(p.currentByte) {
|
||||
if !isValidLabelNameContinuation(p.currentByte, quoted) || (!quoted && p.currentByte == '=') {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -658,6 +870,7 @@ func (p *TextParser) readTokenAsLabelValue() {
|
||||
p.currentToken.WriteByte('\n')
|
||||
default:
|
||||
p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
|
||||
p.currentLabelPairs = nil
|
||||
return
|
||||
}
|
||||
escaped = false
|
||||
@@ -683,6 +896,10 @@ func (p *TextParser) setOrCreateCurrentMF() {
|
||||
p.currentIsHistogramCount = false
|
||||
p.currentIsHistogramSum = false
|
||||
name := p.currentToken.String()
|
||||
if !p.scheme.IsValidMetricName(name) {
|
||||
p.parseError(fmt.Sprintf("invalid metric name %q", name))
|
||||
return
|
||||
}
|
||||
if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil {
|
||||
return
|
||||
}
|
||||
@@ -701,7 +918,8 @@ func (p *TextParser) setOrCreateCurrentMF() {
|
||||
}
|
||||
histogramName := histogramMetricName(name)
|
||||
if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil {
|
||||
if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
|
||||
if p.currentMF.GetType() == dto.MetricType_HISTOGRAM ||
|
||||
p.currentMF.GetType() == dto.MetricType_GAUGE_HISTOGRAM {
|
||||
if isCount(name) {
|
||||
p.currentIsHistogramCount = true
|
||||
}
|
||||
@@ -716,19 +934,19 @@ func (p *TextParser) setOrCreateCurrentMF() {
|
||||
}
|
||||
|
||||
func isValidLabelNameStart(b byte) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == '"'
|
||||
}
|
||||
|
||||
func isValidLabelNameContinuation(b byte) bool {
|
||||
return isValidLabelNameStart(b) || (b >= '0' && b <= '9')
|
||||
func isValidLabelNameContinuation(b byte, quoted bool) bool {
|
||||
return isValidLabelNameStart(b) || (b >= '0' && b <= '9') || (quoted && utf8.ValidString(string(b)))
|
||||
}
|
||||
|
||||
func isValidMetricNameStart(b byte) bool {
|
||||
return isValidLabelNameStart(b) || b == ':'
|
||||
}
|
||||
|
||||
func isValidMetricNameContinuation(b byte) bool {
|
||||
return isValidLabelNameContinuation(b) || b == ':'
|
||||
func isValidMetricNameContinuation(b byte, quoted bool) bool {
|
||||
return isValidLabelNameContinuation(b, quoted) || b == ':'
|
||||
}
|
||||
|
||||
func isBlankOrTab(b byte) bool {
|
||||
@@ -773,7 +991,7 @@ func histogramMetricName(name string) string {
|
||||
|
||||
func parseFloat(s string) (float64, error) {
|
||||
if strings.ContainsAny(s, "pP_") {
|
||||
return 0, fmt.Errorf("unsupported character in float")
|
||||
return 0, errors.New("unsupported character in float")
|
||||
}
|
||||
return strconv.ParseFloat(s, 64)
|
||||
}
|
||||
|
||||
67
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/README.txt
generated
vendored
67
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/README.txt
generated
vendored
@@ -1,67 +0,0 @@
|
||||
PACKAGE
|
||||
|
||||
package goautoneg
|
||||
import "bitbucket.org/ww/goautoneg"
|
||||
|
||||
HTTP Content-Type Autonegotiation.
|
||||
|
||||
The functions in this package implement the behaviour specified in
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
|
||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the name of the Open Knowledge Foundation Ltd. nor the
|
||||
names of its contributors may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
|
||||
func Negotiate(header string, alternatives []string) (content_type string)
|
||||
Negotiate the most appropriate content_type given the accept header
|
||||
and a list of alternatives.
|
||||
|
||||
func ParseAccept(header string) (accept []Accept)
|
||||
Parse an Accept Header string returning a sorted list
|
||||
of clauses
|
||||
|
||||
|
||||
TYPES
|
||||
|
||||
type Accept struct {
|
||||
Type, SubType string
|
||||
Q float32
|
||||
Params map[string]string
|
||||
}
|
||||
Structure to represent a clause in an HTTP Accept Header
|
||||
|
||||
|
||||
SUBDIRECTORIES
|
||||
|
||||
.hg
|
||||
160
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
160
vendor/github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg/autoneg.go
generated
vendored
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2011, Open Knowledge Foundation Ltd.
|
||||
All rights reserved.
|
||||
|
||||
HTTP Content-Type Autonegotiation.
|
||||
|
||||
The functions in this package implement the behaviour specified in
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
Neither the name of the Open Knowledge Foundation Ltd. nor the
|
||||
names of its contributors may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package goautoneg
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Structure to represent a clause in an HTTP Accept Header
|
||||
type Accept struct {
|
||||
Type, SubType string
|
||||
Q float64
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
// For internal use, so that we can use the sort interface
|
||||
type accept_slice []Accept
|
||||
|
||||
func (accept accept_slice) Len() int {
|
||||
slice := []Accept(accept)
|
||||
return len(slice)
|
||||
}
|
||||
|
||||
func (accept accept_slice) Less(i, j int) bool {
|
||||
slice := []Accept(accept)
|
||||
ai, aj := slice[i], slice[j]
|
||||
if ai.Q > aj.Q {
|
||||
return true
|
||||
}
|
||||
if ai.Type != "*" && aj.Type == "*" {
|
||||
return true
|
||||
}
|
||||
if ai.SubType != "*" && aj.SubType == "*" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (accept accept_slice) Swap(i, j int) {
|
||||
slice := []Accept(accept)
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
|
||||
// Parse an Accept Header string returning a sorted list
|
||||
// of clauses
|
||||
func ParseAccept(header string) (accept []Accept) {
|
||||
parts := strings.Split(header, ",")
|
||||
accept = make([]Accept, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
part := strings.Trim(part, " ")
|
||||
|
||||
a := Accept{}
|
||||
a.Params = make(map[string]string)
|
||||
a.Q = 1.0
|
||||
|
||||
mrp := strings.Split(part, ";")
|
||||
|
||||
media_range := mrp[0]
|
||||
sp := strings.Split(media_range, "/")
|
||||
a.Type = strings.Trim(sp[0], " ")
|
||||
|
||||
switch {
|
||||
case len(sp) == 1 && a.Type == "*":
|
||||
a.SubType = "*"
|
||||
case len(sp) == 2:
|
||||
a.SubType = strings.Trim(sp[1], " ")
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if len(mrp) == 1 {
|
||||
accept = append(accept, a)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, param := range mrp[1:] {
|
||||
sp := strings.SplitN(param, "=", 2)
|
||||
if len(sp) != 2 {
|
||||
continue
|
||||
}
|
||||
token := strings.Trim(sp[0], " ")
|
||||
if token == "q" {
|
||||
a.Q, _ = strconv.ParseFloat(sp[1], 32)
|
||||
} else {
|
||||
a.Params[token] = strings.Trim(sp[1], " ")
|
||||
}
|
||||
}
|
||||
|
||||
accept = append(accept, a)
|
||||
}
|
||||
|
||||
slice := accept_slice(accept)
|
||||
sort.Sort(slice)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Negotiate the most appropriate content_type given the accept header
|
||||
// and a list of alternatives.
|
||||
func Negotiate(header string, alternatives []string) (content_type string) {
|
||||
asp := make([][]string, 0, len(alternatives))
|
||||
for _, ctype := range alternatives {
|
||||
asp = append(asp, strings.SplitN(ctype, "/", 2))
|
||||
}
|
||||
for _, clause := range ParseAccept(header) {
|
||||
for i, ctsp := range asp {
|
||||
if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
|
||||
content_type = alternatives[i]
|
||||
return
|
||||
}
|
||||
if clause.Type == ctsp[0] && clause.SubType == "*" {
|
||||
content_type = alternatives[i]
|
||||
return
|
||||
}
|
||||
if clause.Type == "*" && clause.SubType == "*" {
|
||||
content_type = alternatives[i]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
40
vendor/github.com/prometheus/common/model/alert.go
generated
vendored
40
vendor/github.com/prometheus/common/model/alert.go
generated
vendored
@@ -14,6 +14,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
@@ -64,7 +65,7 @@ func (a *Alert) Resolved() bool {
|
||||
return a.ResolvedAt(time.Now())
|
||||
}
|
||||
|
||||
// ResolvedAt returns true off the activity interval ended before
|
||||
// ResolvedAt returns true iff the activity interval ended before
|
||||
// the given timestamp.
|
||||
func (a *Alert) ResolvedAt(ts time.Time) bool {
|
||||
if a.EndsAt.IsZero() {
|
||||
@@ -75,7 +76,12 @@ func (a *Alert) ResolvedAt(ts time.Time) bool {
|
||||
|
||||
// Status returns the status of the alert.
|
||||
func (a *Alert) Status() AlertStatus {
|
||||
if a.Resolved() {
|
||||
return a.StatusAt(time.Now())
|
||||
}
|
||||
|
||||
// StatusAt returns the status of the alert at the given timestamp.
|
||||
func (a *Alert) StatusAt(ts time.Time) AlertStatus {
|
||||
if a.ResolvedAt(ts) {
|
||||
return AlertResolved
|
||||
}
|
||||
return AlertFiring
|
||||
@@ -84,19 +90,19 @@ func (a *Alert) Status() AlertStatus {
|
||||
// Validate checks whether the alert data is inconsistent.
|
||||
func (a *Alert) Validate() error {
|
||||
if a.StartsAt.IsZero() {
|
||||
return fmt.Errorf("start time missing")
|
||||
return errors.New("start time missing")
|
||||
}
|
||||
if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) {
|
||||
return fmt.Errorf("start time must be before end time")
|
||||
return errors.New("start time must be before end time")
|
||||
}
|
||||
if err := a.Labels.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid label set: %s", err)
|
||||
return fmt.Errorf("invalid label set: %w", err)
|
||||
}
|
||||
if len(a.Labels) == 0 {
|
||||
return fmt.Errorf("at least one label pair required")
|
||||
return errors.New("at least one label pair required")
|
||||
}
|
||||
if err := a.Annotations.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid annotations: %s", err)
|
||||
return fmt.Errorf("invalid annotations: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -127,6 +133,17 @@ func (as Alerts) HasFiring() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasFiringAt returns true iff one of the alerts is not resolved
|
||||
// at the time ts.
|
||||
func (as Alerts) HasFiringAt(ts time.Time) bool {
|
||||
for _, a := range as {
|
||||
if !a.ResolvedAt(ts) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Status returns StatusFiring iff at least one of the alerts is firing.
|
||||
func (as Alerts) Status() AlertStatus {
|
||||
if as.HasFiring() {
|
||||
@@ -134,3 +151,12 @@ func (as Alerts) Status() AlertStatus {
|
||||
}
|
||||
return AlertResolved
|
||||
}
|
||||
|
||||
// StatusAt returns StatusFiring iff at least one of the alerts is firing
|
||||
// at the time ts.
|
||||
func (as Alerts) StatusAt(ts time.Time) AlertStatus {
|
||||
if as.HasFiringAt(ts) {
|
||||
return AlertFiring
|
||||
}
|
||||
return AlertResolved
|
||||
}
|
||||
|
||||
39
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
39
vendor/github.com/prometheus/common/model/labels.go
generated
vendored
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// AlertNameLabel is the name of the label containing the an alert's name.
|
||||
// AlertNameLabel is the name of the label containing the alert's name.
|
||||
AlertNameLabel = "alertname"
|
||||
|
||||
// ExportedLabelPrefix is the prefix to prepend to the label names present in
|
||||
@@ -32,6 +32,12 @@ const (
|
||||
// MetricNameLabel is the label name indicating the metric name of a
|
||||
// timeseries.
|
||||
MetricNameLabel = "__name__"
|
||||
// MetricTypeLabel is the label name indicating the metric type of
|
||||
// timeseries as per the PROM-39 proposal.
|
||||
MetricTypeLabel = "__type__"
|
||||
// MetricUnitLabel is the label name indicating the metric unit of
|
||||
// timeseries as per the PROM-39 proposal.
|
||||
MetricUnitLabel = "__unit__"
|
||||
|
||||
// SchemeLabel is the name of the label that holds the scheme on which to
|
||||
// scrape a target.
|
||||
@@ -97,19 +103,24 @@ var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
|
||||
// therewith.
|
||||
type LabelName string
|
||||
|
||||
// IsValid is true iff the label name matches the pattern of LabelNameRE. This
|
||||
// method, however, does not use LabelNameRE for the check but a much faster
|
||||
// hardcoded implementation.
|
||||
// IsValid returns true iff the name matches the pattern of LabelNameRE when
|
||||
// NameValidationScheme is set to LegacyValidation, or valid UTF-8 if
|
||||
// NameValidationScheme is set to UTF8Validation.
|
||||
//
|
||||
// Deprecated: This method should not be used and may be removed in the future.
|
||||
// Use [ValidationScheme.IsValidLabelName] instead.
|
||||
func (ln LabelName) IsValid() bool {
|
||||
if len(ln) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, b := range ln {
|
||||
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return NameValidationScheme.IsValidLabelName(string(ln))
|
||||
}
|
||||
|
||||
// IsValidLegacy returns true iff name matches the pattern of LabelNameRE for
|
||||
// legacy names. It does not use LabelNameRE for the check but a much faster
|
||||
// hardcoded implementation.
|
||||
//
|
||||
// Deprecated: This method should not be used and may be removed in the future.
|
||||
// Use [LegacyValidation.IsValidLabelName] instead.
|
||||
func (ln LabelName) IsValidLegacy() bool {
|
||||
return LegacyValidation.IsValidLabelName(string(ln))
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
@@ -164,7 +175,7 @@ func (l LabelNames) String() string {
|
||||
// A LabelValue is an associated value for a LabelName.
|
||||
type LabelValue string
|
||||
|
||||
// IsValid returns true iff the string is a valid UTF8.
|
||||
// IsValid returns true iff the string is a valid UTF-8.
|
||||
func (lv LabelValue) IsValid() bool {
|
||||
return utf8.ValidString(string(lv))
|
||||
}
|
||||
|
||||
21
vendor/github.com/prometheus/common/model/labelset.go
generated
vendored
21
vendor/github.com/prometheus/common/model/labelset.go
generated
vendored
@@ -17,7 +17,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet
|
||||
@@ -115,10 +114,10 @@ func (ls LabelSet) Clone() LabelSet {
|
||||
}
|
||||
|
||||
// Merge is a helper function to non-destructively merge two label sets.
|
||||
func (l LabelSet) Merge(other LabelSet) LabelSet {
|
||||
result := make(LabelSet, len(l))
|
||||
func (ls LabelSet) Merge(other LabelSet) LabelSet {
|
||||
result := make(LabelSet, len(ls))
|
||||
|
||||
for k, v := range l {
|
||||
for k, v := range ls {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
@@ -129,16 +128,6 @@ func (l LabelSet) Merge(other LabelSet) LabelSet {
|
||||
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)
|
||||
@@ -151,7 +140,7 @@ func (ls LabelSet) FastFingerprint() Fingerprint {
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (l *LabelSet) UnmarshalJSON(b []byte) error {
|
||||
func (ls *LabelSet) UnmarshalJSON(b []byte) error {
|
||||
var m map[LabelName]LabelValue
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return err
|
||||
@@ -164,6 +153,6 @@ func (l *LabelSet) UnmarshalJSON(b []byte) error {
|
||||
return fmt.Errorf("%q is not a valid label name", ln)
|
||||
}
|
||||
}
|
||||
*l = LabelSet(m)
|
||||
*ls = LabelSet(m)
|
||||
return nil
|
||||
}
|
||||
|
||||
43
vendor/github.com/prometheus/common/model/labelset_string.go
generated
vendored
Normal file
43
vendor/github.com/prometheus/common/model/labelset_string.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2024 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 (
|
||||
"bytes"
|
||||
"slices"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// String will look like `{foo="bar", more="less"}`. Names are sorted alphabetically.
|
||||
func (l LabelSet) String() string {
|
||||
var lna [32]string // On stack to avoid memory allocation for sorting names.
|
||||
labelNames := lna[:0]
|
||||
for name := range l {
|
||||
labelNames = append(labelNames, string(name))
|
||||
}
|
||||
slices.Sort(labelNames)
|
||||
var bytea [1024]byte // On stack to avoid memory allocation while building the output.
|
||||
b := bytes.NewBuffer(bytea[:0])
|
||||
b.WriteByte('{')
|
||||
for i, name := range labelNames {
|
||||
if i > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString(name)
|
||||
b.WriteByte('=')
|
||||
b.Write(strconv.AppendQuote(b.AvailableBuffer(), string(l[LabelName(name)])))
|
||||
}
|
||||
b.WriteByte('}')
|
||||
return b.String()
|
||||
}
|
||||
28
vendor/github.com/prometheus/common/model/metadata.go
generated
vendored
Normal file
28
vendor/github.com/prometheus/common/model/metadata.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2023 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
|
||||
|
||||
// MetricType represents metric type values.
|
||||
type MetricType string
|
||||
|
||||
const (
|
||||
MetricTypeCounter = MetricType("counter")
|
||||
MetricTypeGauge = MetricType("gauge")
|
||||
MetricTypeHistogram = MetricType("histogram")
|
||||
MetricTypeGaugeHistogram = MetricType("gaugehistogram")
|
||||
MetricTypeSummary = MetricType("summary")
|
||||
MetricTypeInfo = MetricType("info")
|
||||
MetricTypeStateset = MetricType("stateset")
|
||||
MetricTypeUnknown = MetricType("unknown")
|
||||
)
|
||||
515
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
515
vendor/github.com/prometheus/common/model/metric.go
generated
vendored
@@ -14,19 +14,242 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"go.yaml.in/yaml/v2"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
// MetricNameRE is a regular expression matching valid metric
|
||||
// names. Note that the IsValidMetricName function performs the same
|
||||
// check but faster than a match with this regular expression.
|
||||
MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
|
||||
// NameValidationScheme determines the global default method of the name
|
||||
// validation to be used by all calls to IsValidMetricName() and LabelName
|
||||
// IsValid().
|
||||
//
|
||||
// Deprecated: This variable should not be used and might be removed in the
|
||||
// far future. If you wish to stick to the legacy name validation use
|
||||
// `IsValidLegacyMetricName()` and `LabelName.IsValidLegacy()` methods
|
||||
// instead. This variable is here as an escape hatch for emergency cases,
|
||||
// given the recent change from `LegacyValidation` to `UTF8Validation`, e.g.,
|
||||
// to delay UTF-8 migrations in time or aid in debugging unforeseen results of
|
||||
// the change. In such a case, a temporary assignment to `LegacyValidation`
|
||||
// value in the `init()` function in your main.go or so, could be considered.
|
||||
//
|
||||
// Historically we opted for a global variable for feature gating different
|
||||
// validation schemes in operations that were not otherwise easily adjustable
|
||||
// (e.g. Labels yaml unmarshaling). That could have been a mistake, a separate
|
||||
// Labels structure or package might have been a better choice. Given the
|
||||
// change was made and many upgraded the common already, we live this as-is
|
||||
// with this warning and learning for the future.
|
||||
NameValidationScheme = UTF8Validation
|
||||
|
||||
// NameEscapingScheme defines the default way that names will be escaped when
|
||||
// presented to systems that do not support UTF-8 names. If the Content-Type
|
||||
// "escaping" term is specified, that will override this value.
|
||||
// NameEscapingScheme should not be set to the NoEscaping value. That string
|
||||
// is used in content negotiation to indicate that a system supports UTF-8 and
|
||||
// has that feature enabled.
|
||||
NameEscapingScheme = UnderscoreEscaping
|
||||
)
|
||||
|
||||
// ValidationScheme is a Go enum for determining how metric and label names will
|
||||
// be validated by this library.
|
||||
type ValidationScheme int
|
||||
|
||||
const (
|
||||
// UnsetValidation represents an undefined ValidationScheme.
|
||||
// Should not be used in practice.
|
||||
UnsetValidation ValidationScheme = iota
|
||||
|
||||
// LegacyValidation is a setting that requires that all metric and label names
|
||||
// conform to the original Prometheus character requirements described by
|
||||
// MetricNameRE and LabelNameRE.
|
||||
LegacyValidation
|
||||
|
||||
// UTF8Validation only requires that metric and label names be valid UTF-8
|
||||
// strings.
|
||||
UTF8Validation
|
||||
)
|
||||
|
||||
var _ interface {
|
||||
yaml.Marshaler
|
||||
yaml.Unmarshaler
|
||||
json.Marshaler
|
||||
json.Unmarshaler
|
||||
fmt.Stringer
|
||||
} = new(ValidationScheme)
|
||||
|
||||
// String returns the string representation of s.
|
||||
func (s ValidationScheme) String() string {
|
||||
switch s {
|
||||
case UnsetValidation:
|
||||
return "unset"
|
||||
case LegacyValidation:
|
||||
return "legacy"
|
||||
case UTF8Validation:
|
||||
return "utf8"
|
||||
default:
|
||||
panic(fmt.Errorf("unhandled ValidationScheme: %d", s))
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (s ValidationScheme) MarshalYAML() (any, error) {
|
||||
switch s {
|
||||
case UnsetValidation:
|
||||
return "", nil
|
||||
case LegacyValidation, UTF8Validation:
|
||||
return s.String(), nil
|
||||
default:
|
||||
panic(fmt.Errorf("unhandled ValidationScheme: %d", s))
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (s *ValidationScheme) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var scheme string
|
||||
if err := unmarshal(&scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Set(scheme)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (s ValidationScheme) MarshalJSON() ([]byte, error) {
|
||||
switch s {
|
||||
case UnsetValidation:
|
||||
return json.Marshal("")
|
||||
case UTF8Validation, LegacyValidation:
|
||||
return json.Marshal(s.String())
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled ValidationScheme: %d", s)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (s *ValidationScheme) UnmarshalJSON(bytes []byte) error {
|
||||
var repr string
|
||||
if err := json.Unmarshal(bytes, &repr); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.Set(repr)
|
||||
}
|
||||
|
||||
// Set implements the pflag.Value interface.
|
||||
func (s *ValidationScheme) Set(text string) error {
|
||||
switch text {
|
||||
case "":
|
||||
// Don't change the value.
|
||||
case LegacyValidation.String():
|
||||
*s = LegacyValidation
|
||||
case UTF8Validation.String():
|
||||
*s = UTF8Validation
|
||||
default:
|
||||
return fmt.Errorf("unrecognized ValidationScheme: %q", text)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsValidMetricName returns whether metricName is valid according to s.
|
||||
func (s ValidationScheme) IsValidMetricName(metricName string) bool {
|
||||
switch s {
|
||||
case LegacyValidation:
|
||||
if len(metricName) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, b := range metricName {
|
||||
if !isValidLegacyRune(b, i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case UTF8Validation:
|
||||
if len(metricName) == 0 {
|
||||
return false
|
||||
}
|
||||
return utf8.ValidString(metricName)
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid name validation scheme requested: %s", s.String()))
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidLabelName returns whether labelName is valid according to s.
|
||||
func (s ValidationScheme) IsValidLabelName(labelName string) bool {
|
||||
switch s {
|
||||
case LegacyValidation:
|
||||
if len(labelName) == 0 {
|
||||
return false
|
||||
}
|
||||
for i, b := range labelName {
|
||||
// TODO: Apply De Morgan's law. Make sure there are tests for this.
|
||||
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || (b >= '0' && b <= '9' && i > 0)) { //nolint:staticcheck
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case UTF8Validation:
|
||||
if len(labelName) == 0 {
|
||||
return false
|
||||
}
|
||||
return utf8.ValidString(labelName)
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid name validation scheme requested: %s", s))
|
||||
}
|
||||
}
|
||||
|
||||
// Type implements the pflag.Value interface.
|
||||
func (ValidationScheme) Type() string {
|
||||
return "validationScheme"
|
||||
}
|
||||
|
||||
type EscapingScheme int
|
||||
|
||||
const (
|
||||
// NoEscaping indicates that a name will not be escaped. Unescaped names that
|
||||
// do not conform to the legacy validity check will use a new exposition
|
||||
// format syntax that will be officially standardized in future versions.
|
||||
NoEscaping EscapingScheme = iota
|
||||
|
||||
// UnderscoreEscaping replaces all legacy-invalid characters with underscores.
|
||||
UnderscoreEscaping
|
||||
|
||||
// DotsEscaping is similar to UnderscoreEscaping, except that dots are
|
||||
// converted to `_dot_` and pre-existing underscores are converted to `__`.
|
||||
DotsEscaping
|
||||
|
||||
// ValueEncodingEscaping prepends the name with `U__` and replaces all invalid
|
||||
// characters with the unicode value, surrounded by underscores. Single
|
||||
// underscores are replaced with double underscores.
|
||||
ValueEncodingEscaping
|
||||
)
|
||||
|
||||
const (
|
||||
// EscapingKey is the key in an Accept or Content-Type header that defines how
|
||||
// metric and label names that do not conform to the legacy character
|
||||
// requirements should be escaped when being scraped by a legacy prometheus
|
||||
// system. If a system does not explicitly pass an escaping parameter in the
|
||||
// Accept header, the default NameEscapingScheme will be used.
|
||||
EscapingKey = "escaping"
|
||||
|
||||
// Possible values for Escaping Key.
|
||||
AllowUTF8 = "allow-utf-8" // No escaping required.
|
||||
EscapeUnderscores = "underscores"
|
||||
EscapeDots = "dots"
|
||||
EscapeValues = "values"
|
||||
)
|
||||
|
||||
// MetricNameRE is a regular expression matching valid metric
|
||||
// names. Note that the IsValidMetricName function performs the same
|
||||
// check but faster than a match with this regular expression.
|
||||
var MetricNameRE = regexp.MustCompile(`^[a-zA-Z_:][a-zA-Z0-9_:]*$`)
|
||||
|
||||
// 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
|
||||
@@ -86,17 +309,285 @@ func (m Metric) FastFingerprint() Fingerprint {
|
||||
return LabelSet(m).FastFingerprint()
|
||||
}
|
||||
|
||||
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE.
|
||||
// IsValidMetricName returns true iff name matches the pattern of MetricNameRE
|
||||
// for legacy names, and iff it's valid UTF-8 if the UTF8Validation scheme is
|
||||
// selected.
|
||||
//
|
||||
// Deprecated: This function should not be used and might be removed in the future.
|
||||
// Use [ValidationScheme.IsValidMetricName] instead.
|
||||
func IsValidMetricName(n LabelValue) bool {
|
||||
return NameValidationScheme.IsValidMetricName(string(n))
|
||||
}
|
||||
|
||||
// IsValidLegacyMetricName is similar to IsValidMetricName but always uses the
|
||||
// legacy validation scheme regardless of the value of NameValidationScheme.
|
||||
// This function, however, does not use MetricNameRE for the check but a much
|
||||
// faster hardcoded implementation.
|
||||
func IsValidMetricName(n LabelValue) bool {
|
||||
if len(n) == 0 {
|
||||
return false
|
||||
//
|
||||
// Deprecated: This function should not be used and might be removed in the future.
|
||||
// Use [LegacyValidation.IsValidMetricName] instead.
|
||||
func IsValidLegacyMetricName(n string) bool {
|
||||
return LegacyValidation.IsValidMetricName(n)
|
||||
}
|
||||
|
||||
// EscapeMetricFamily escapes the given metric names and labels with the given
|
||||
// escaping scheme. Returns a new object that uses the same pointers to fields
|
||||
// when possible and creates new escaped versions so as not to mutate the
|
||||
// input.
|
||||
func EscapeMetricFamily(v *dto.MetricFamily, scheme EscapingScheme) *dto.MetricFamily {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
for i, b := range n {
|
||||
if !((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)) {
|
||||
return false
|
||||
|
||||
if scheme == NoEscaping {
|
||||
return v
|
||||
}
|
||||
|
||||
out := &dto.MetricFamily{
|
||||
Help: v.Help,
|
||||
Type: v.Type,
|
||||
Unit: v.Unit,
|
||||
}
|
||||
|
||||
// If the name is nil, copy as-is, don't try to escape.
|
||||
if v.Name == nil || IsValidLegacyMetricName(v.GetName()) {
|
||||
out.Name = v.Name
|
||||
} else {
|
||||
out.Name = proto.String(EscapeName(v.GetName(), scheme))
|
||||
}
|
||||
for _, m := range v.Metric {
|
||||
if !metricNeedsEscaping(m) {
|
||||
out.Metric = append(out.Metric, m)
|
||||
continue
|
||||
}
|
||||
|
||||
escaped := &dto.Metric{
|
||||
Gauge: m.Gauge,
|
||||
Counter: m.Counter,
|
||||
Summary: m.Summary,
|
||||
Untyped: m.Untyped,
|
||||
Histogram: m.Histogram,
|
||||
TimestampMs: m.TimestampMs,
|
||||
}
|
||||
|
||||
for _, l := range m.Label {
|
||||
if l.GetName() == MetricNameLabel {
|
||||
if l.Value == nil || IsValidLegacyMetricName(l.GetValue()) {
|
||||
escaped.Label = append(escaped.Label, l)
|
||||
continue
|
||||
}
|
||||
escaped.Label = append(escaped.Label, &dto.LabelPair{
|
||||
Name: proto.String(MetricNameLabel),
|
||||
Value: proto.String(EscapeName(l.GetValue(), scheme)),
|
||||
})
|
||||
continue
|
||||
}
|
||||
if l.Name == nil || IsValidLegacyMetricName(l.GetName()) {
|
||||
escaped.Label = append(escaped.Label, l)
|
||||
continue
|
||||
}
|
||||
escaped.Label = append(escaped.Label, &dto.LabelPair{
|
||||
Name: proto.String(EscapeName(l.GetName(), scheme)),
|
||||
Value: l.Value,
|
||||
})
|
||||
}
|
||||
out.Metric = append(out.Metric, escaped)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func metricNeedsEscaping(m *dto.Metric) bool {
|
||||
for _, l := range m.Label {
|
||||
if l.GetName() == MetricNameLabel && !IsValidLegacyMetricName(l.GetValue()) {
|
||||
return true
|
||||
}
|
||||
if !IsValidLegacyMetricName(l.GetName()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
// EscapeName escapes the incoming name according to the provided escaping
|
||||
// scheme. Depending on the rules of escaping, this may cause no change in the
|
||||
// string that is returned. (Especially NoEscaping, which by definition is a
|
||||
// noop). This function does not do any validation of the name.
|
||||
func EscapeName(name string, scheme EscapingScheme) string {
|
||||
if len(name) == 0 {
|
||||
return name
|
||||
}
|
||||
var escaped strings.Builder
|
||||
switch scheme {
|
||||
case NoEscaping:
|
||||
return name
|
||||
case UnderscoreEscaping:
|
||||
if IsValidLegacyMetricName(name) {
|
||||
return name
|
||||
}
|
||||
for i, b := range name {
|
||||
if isValidLegacyRune(b, i) {
|
||||
escaped.WriteRune(b)
|
||||
} else {
|
||||
escaped.WriteRune('_')
|
||||
}
|
||||
}
|
||||
return escaped.String()
|
||||
case DotsEscaping:
|
||||
// Do not early return for legacy valid names, we still escape underscores.
|
||||
for i, b := range name {
|
||||
switch {
|
||||
case b == '_':
|
||||
escaped.WriteString("__")
|
||||
case b == '.':
|
||||
escaped.WriteString("_dot_")
|
||||
case isValidLegacyRune(b, i):
|
||||
escaped.WriteRune(b)
|
||||
default:
|
||||
escaped.WriteString("__")
|
||||
}
|
||||
}
|
||||
return escaped.String()
|
||||
case ValueEncodingEscaping:
|
||||
if IsValidLegacyMetricName(name) {
|
||||
return name
|
||||
}
|
||||
escaped.WriteString("U__")
|
||||
for i, b := range name {
|
||||
switch {
|
||||
case b == '_':
|
||||
escaped.WriteString("__")
|
||||
case isValidLegacyRune(b, i):
|
||||
escaped.WriteRune(b)
|
||||
case !utf8.ValidRune(b):
|
||||
escaped.WriteString("_FFFD_")
|
||||
default:
|
||||
escaped.WriteRune('_')
|
||||
escaped.WriteString(strconv.FormatInt(int64(b), 16))
|
||||
escaped.WriteRune('_')
|
||||
}
|
||||
}
|
||||
return escaped.String()
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid escaping scheme %d", scheme))
|
||||
}
|
||||
}
|
||||
|
||||
// lower function taken from strconv.atoi.
|
||||
func lower(c byte) byte {
|
||||
return c | ('x' - 'X')
|
||||
}
|
||||
|
||||
// UnescapeName unescapes the incoming name according to the provided escaping
|
||||
// scheme if possible. Some schemes are partially or totally non-roundtripable.
|
||||
// If any error is enountered, returns the original input.
|
||||
func UnescapeName(name string, scheme EscapingScheme) string {
|
||||
if len(name) == 0 {
|
||||
return name
|
||||
}
|
||||
switch scheme {
|
||||
case NoEscaping:
|
||||
return name
|
||||
case UnderscoreEscaping:
|
||||
// It is not possible to unescape from underscore replacement.
|
||||
return name
|
||||
case DotsEscaping:
|
||||
name = strings.ReplaceAll(name, "_dot_", ".")
|
||||
name = strings.ReplaceAll(name, "__", "_")
|
||||
return name
|
||||
case ValueEncodingEscaping:
|
||||
escapedName, found := strings.CutPrefix(name, "U__")
|
||||
if !found {
|
||||
return name
|
||||
}
|
||||
|
||||
var unescaped strings.Builder
|
||||
TOP:
|
||||
for i := 0; i < len(escapedName); i++ {
|
||||
// All non-underscores are treated normally.
|
||||
if escapedName[i] != '_' {
|
||||
unescaped.WriteByte(escapedName[i])
|
||||
continue
|
||||
}
|
||||
i++
|
||||
if i >= len(escapedName) {
|
||||
return name
|
||||
}
|
||||
// A double underscore is a single underscore.
|
||||
if escapedName[i] == '_' {
|
||||
unescaped.WriteByte('_')
|
||||
continue
|
||||
}
|
||||
// We think we are in a UTF-8 code, process it.
|
||||
var utf8Val uint
|
||||
for j := 0; i < len(escapedName); j++ {
|
||||
// This is too many characters for a utf8 value based on the MaxRune
|
||||
// value of '\U0010FFFF'.
|
||||
if j >= 6 {
|
||||
return name
|
||||
}
|
||||
// Found a closing underscore, convert to a rune, check validity, and append.
|
||||
if escapedName[i] == '_' {
|
||||
utf8Rune := rune(utf8Val)
|
||||
if !utf8.ValidRune(utf8Rune) {
|
||||
return name
|
||||
}
|
||||
unescaped.WriteRune(utf8Rune)
|
||||
continue TOP
|
||||
}
|
||||
r := lower(escapedName[i])
|
||||
utf8Val *= 16
|
||||
switch {
|
||||
case r >= '0' && r <= '9':
|
||||
utf8Val += uint(r) - '0'
|
||||
case r >= 'a' && r <= 'f':
|
||||
utf8Val += uint(r) - 'a' + 10
|
||||
default:
|
||||
return name
|
||||
}
|
||||
i++
|
||||
}
|
||||
// Didn't find closing underscore, invalid.
|
||||
return name
|
||||
}
|
||||
return unescaped.String()
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid escaping scheme %d", scheme))
|
||||
}
|
||||
}
|
||||
|
||||
func isValidLegacyRune(b rune, i int) bool {
|
||||
return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' || b == ':' || (b >= '0' && b <= '9' && i > 0)
|
||||
}
|
||||
|
||||
func (e EscapingScheme) String() string {
|
||||
switch e {
|
||||
case NoEscaping:
|
||||
return AllowUTF8
|
||||
case UnderscoreEscaping:
|
||||
return EscapeUnderscores
|
||||
case DotsEscaping:
|
||||
return EscapeDots
|
||||
case ValueEncodingEscaping:
|
||||
return EscapeValues
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown format scheme %d", e))
|
||||
}
|
||||
}
|
||||
|
||||
func ToEscapingScheme(s string) (EscapingScheme, error) {
|
||||
if s == "" {
|
||||
return NoEscaping, errors.New("got empty string instead of escaping scheme")
|
||||
}
|
||||
switch s {
|
||||
case AllowUTF8:
|
||||
return NoEscaping, nil
|
||||
case EscapeUnderscores:
|
||||
return UnderscoreEscaping, nil
|
||||
case EscapeDots:
|
||||
return DotsEscaping, nil
|
||||
case EscapeValues:
|
||||
return ValueEncodingEscaping, nil
|
||||
default:
|
||||
return NoEscaping, fmt.Errorf("unknown format scheme %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
6
vendor/github.com/prometheus/common/model/signature.go
generated
vendored
6
vendor/github.com/prometheus/common/model/signature.go
generated
vendored
@@ -22,10 +22,8 @@ import (
|
||||
// when calculating their combined hash value (aka signature aka fingerprint).
|
||||
const SeparatorByte byte = 255
|
||||
|
||||
var (
|
||||
// cache the signature of an empty label set.
|
||||
emptyLabelSignature = hashNew()
|
||||
)
|
||||
// cache the signature of an empty label set.
|
||||
var emptyLabelSignature = hashNew()
|
||||
|
||||
// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a
|
||||
// given label set. (Collisions are possible but unlikely if the number of label
|
||||
|
||||
19
vendor/github.com/prometheus/common/model/silence.go
generated
vendored
19
vendor/github.com/prometheus/common/model/silence.go
generated
vendored
@@ -15,6 +15,7 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"time"
|
||||
@@ -34,7 +35,7 @@ func (m *Matcher) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
if len(m.Name) == 0 {
|
||||
return fmt.Errorf("label name in matcher must not be empty")
|
||||
return errors.New("label name in matcher must not be empty")
|
||||
}
|
||||
if m.IsRegex {
|
||||
if _, err := regexp.Compile(m.Value); err != nil {
|
||||
@@ -77,30 +78,30 @@ type Silence struct {
|
||||
// Validate returns true iff all fields of the silence have valid values.
|
||||
func (s *Silence) Validate() error {
|
||||
if len(s.Matchers) == 0 {
|
||||
return fmt.Errorf("at least one matcher required")
|
||||
return errors.New("at least one matcher required")
|
||||
}
|
||||
for _, m := range s.Matchers {
|
||||
if err := m.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid matcher: %s", err)
|
||||
return fmt.Errorf("invalid matcher: %w", err)
|
||||
}
|
||||
}
|
||||
if s.StartsAt.IsZero() {
|
||||
return fmt.Errorf("start time missing")
|
||||
return errors.New("start time missing")
|
||||
}
|
||||
if s.EndsAt.IsZero() {
|
||||
return fmt.Errorf("end time missing")
|
||||
return errors.New("end time missing")
|
||||
}
|
||||
if s.EndsAt.Before(s.StartsAt) {
|
||||
return fmt.Errorf("start time must be before end time")
|
||||
return errors.New("start time must be before end time")
|
||||
}
|
||||
if s.CreatedBy == "" {
|
||||
return fmt.Errorf("creator information missing")
|
||||
return errors.New("creator information missing")
|
||||
}
|
||||
if s.Comment == "" {
|
||||
return fmt.Errorf("comment missing")
|
||||
return errors.New("comment missing")
|
||||
}
|
||||
if s.CreatedAt.IsZero() {
|
||||
return fmt.Errorf("creation timestamp missing")
|
||||
return errors.New("creation timestamp missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
37
vendor/github.com/prometheus/common/model/time.go
generated
vendored
37
vendor/github.com/prometheus/common/model/time.go
generated
vendored
@@ -126,14 +126,14 @@ 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)
|
||||
v, err := strconv.ParseInt(p[0], 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = Time(v * second)
|
||||
|
||||
case 2:
|
||||
v, err := strconv.ParseInt(string(p[0]), 10, 64)
|
||||
v, err := strconv.ParseInt(p[0], 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -143,7 +143,7 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
||||
if prec < 0 {
|
||||
p[1] = p[1][:dotPrecision]
|
||||
} else if prec > 0 {
|
||||
p[1] = p[1] + strings.Repeat("0", prec)
|
||||
p[1] += strings.Repeat("0", prec)
|
||||
}
|
||||
|
||||
va, err := strconv.ParseInt(p[1], 10, 32)
|
||||
@@ -170,15 +170,15 @@ func (t *Time) UnmarshalJSON(b []byte) error {
|
||||
// This type should not propagate beyond the scope of input/output processing.
|
||||
type Duration time.Duration
|
||||
|
||||
// Set implements pflag/flag.Value
|
||||
// Set implements pflag/flag.Value.
|
||||
func (d *Duration) Set(s string) error {
|
||||
var err error
|
||||
*d, err = ParseDuration(s)
|
||||
return err
|
||||
}
|
||||
|
||||
// Type implements pflag.Value
|
||||
func (d *Duration) Type() string {
|
||||
// Type implements pflag.Value.
|
||||
func (*Duration) Type() string {
|
||||
return "duration"
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ var unitMap = map[string]struct {
|
||||
|
||||
// ParseDuration parses a string into a time.Duration, assuming that a year
|
||||
// always has 365d, a week always has 7d, and a day always has 24h.
|
||||
// Negative durations are not supported.
|
||||
func ParseDuration(s string) (Duration, error) {
|
||||
switch s {
|
||||
case "0":
|
||||
@@ -253,18 +254,36 @@ func ParseDuration(s string) (Duration, error) {
|
||||
return 0, errors.New("duration out of range")
|
||||
}
|
||||
}
|
||||
|
||||
return Duration(dur), nil
|
||||
}
|
||||
|
||||
// ParseDurationAllowNegative is like ParseDuration but also accepts negative durations.
|
||||
func ParseDurationAllowNegative(s string) (Duration, error) {
|
||||
if s == "" || s[0] != '-' {
|
||||
return ParseDuration(s)
|
||||
}
|
||||
|
||||
d, err := ParseDuration(s[1:])
|
||||
|
||||
return -d, err
|
||||
}
|
||||
|
||||
func (d Duration) String() string {
|
||||
var (
|
||||
ms = int64(time.Duration(d) / time.Millisecond)
|
||||
r = ""
|
||||
ms = int64(time.Duration(d) / time.Millisecond)
|
||||
r = ""
|
||||
sign = ""
|
||||
)
|
||||
|
||||
if ms == 0 {
|
||||
return "0s"
|
||||
}
|
||||
|
||||
if ms < 0 {
|
||||
sign, ms = "-", -ms
|
||||
}
|
||||
|
||||
f := func(unit string, mult int64, exact bool) {
|
||||
if exact && ms%mult != 0 {
|
||||
return
|
||||
@@ -286,7 +305,7 @@ func (d Duration) String() string {
|
||||
f("s", 1000, false)
|
||||
f("ms", 1, false)
|
||||
|
||||
return r
|
||||
return sign + r
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
|
||||
31
vendor/github.com/prometheus/common/model/value.go
generated
vendored
31
vendor/github.com/prometheus/common/model/value.go
generated
vendored
@@ -21,14 +21,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ZeroSample is the pseudo zero-value of Sample used to signal a
|
||||
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
|
||||
// and metric nil. Note that the natural zero value of Sample has a timestamp
|
||||
// of 0, which is possible to appear in a real Sample and thus not suitable
|
||||
// to signal a non-existing Sample.
|
||||
ZeroSample = Sample{Timestamp: Earliest}
|
||||
)
|
||||
// ZeroSample is the pseudo zero-value of Sample used to signal a
|
||||
// non-existing sample. It is a Sample with timestamp Earliest, value 0.0,
|
||||
// and metric nil. Note that the natural zero value of Sample has a timestamp
|
||||
// of 0, which is possible to appear in a real Sample and thus not suitable
|
||||
// to signal a non-existing Sample.
|
||||
var ZeroSample = Sample{Timestamp: Earliest}
|
||||
|
||||
// Sample is a sample pair associated with a metric. A single sample must either
|
||||
// define Value or Histogram but not both. Histogram == nil implies the Value
|
||||
@@ -193,7 +191,8 @@ func (ss SampleStream) String() string {
|
||||
}
|
||||
|
||||
func (ss SampleStream) MarshalJSON() ([]byte, error) {
|
||||
if len(ss.Histograms) > 0 && len(ss.Values) > 0 {
|
||||
switch {
|
||||
case len(ss.Histograms) > 0 && len(ss.Values) > 0:
|
||||
v := struct {
|
||||
Metric Metric `json:"metric"`
|
||||
Values []SamplePair `json:"values"`
|
||||
@@ -204,7 +203,7 @@ func (ss SampleStream) MarshalJSON() ([]byte, error) {
|
||||
Histograms: ss.Histograms,
|
||||
}
|
||||
return json.Marshal(&v)
|
||||
} else if len(ss.Histograms) > 0 {
|
||||
case len(ss.Histograms) > 0:
|
||||
v := struct {
|
||||
Metric Metric `json:"metric"`
|
||||
Histograms []SampleHistogramPair `json:"histograms"`
|
||||
@@ -213,7 +212,7 @@ func (ss SampleStream) MarshalJSON() ([]byte, error) {
|
||||
Histograms: ss.Histograms,
|
||||
}
|
||||
return json.Marshal(&v)
|
||||
} else {
|
||||
default:
|
||||
v := struct {
|
||||
Metric Metric `json:"metric"`
|
||||
Values []SamplePair `json:"values"`
|
||||
@@ -260,7 +259,7 @@ func (s Scalar) String() string {
|
||||
// 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)})
|
||||
return json.Marshal([...]interface{}{s.Timestamp, v})
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
@@ -274,7 +273,7 @@ func (s *Scalar) UnmarshalJSON(b []byte) error {
|
||||
|
||||
value, err := strconv.ParseFloat(f, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing sample value: %s", err)
|
||||
return fmt.Errorf("error parsing sample value: %w", err)
|
||||
}
|
||||
s.Value = SampleValue(value)
|
||||
return nil
|
||||
@@ -351,9 +350,9 @@ 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)
|
||||
func (m Matrix) String() string {
|
||||
matCp := make(Matrix, len(m))
|
||||
copy(matCp, m)
|
||||
sort.Sort(matCp)
|
||||
|
||||
strs := make([]string, len(matCp))
|
||||
|
||||
17
vendor/github.com/prometheus/common/model/value_float.go
generated
vendored
17
vendor/github.com/prometheus/common/model/value_float.go
generated
vendored
@@ -15,19 +15,18 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
|
||||
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
|
||||
// value 0.0. Note that the natural zero value of SamplePair has a timestamp
|
||||
// of 0, which is possible to appear in a real SamplePair and thus not
|
||||
// suitable to signal a non-existing SamplePair.
|
||||
ZeroSamplePair = SamplePair{Timestamp: Earliest}
|
||||
)
|
||||
// ZeroSamplePair is the pseudo zero-value of SamplePair used to signal a
|
||||
// non-existing sample pair. It is a SamplePair with timestamp Earliest and
|
||||
// value 0.0. Note that the natural zero value of SamplePair has a timestamp
|
||||
// of 0, which is possible to appear in a real SamplePair and thus not
|
||||
// suitable to signal a non-existing SamplePair.
|
||||
var ZeroSamplePair = SamplePair{Timestamp: Earliest}
|
||||
|
||||
// A SampleValue is a representation of a value for a given sample at a given
|
||||
// time.
|
||||
@@ -41,7 +40,7 @@ func (v SampleValue) MarshalJSON() ([]byte, error) {
|
||||
// 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")
|
||||
return errors.New("sample value must be a quoted string")
|
||||
}
|
||||
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
|
||||
if err != nil {
|
||||
|
||||
17
vendor/github.com/prometheus/common/model/value_histogram.go
generated
vendored
17
vendor/github.com/prometheus/common/model/value_histogram.go
generated
vendored
@@ -15,6 +15,7 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,7 +33,7 @@ func (v FloatString) MarshalJSON() ([]byte, error) {
|
||||
|
||||
func (v *FloatString) UnmarshalJSON(b []byte) error {
|
||||
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
|
||||
return fmt.Errorf("float value must be a quoted string")
|
||||
return errors.New("float value must be a quoted string")
|
||||
}
|
||||
f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
|
||||
if err != nil {
|
||||
@@ -85,22 +86,22 @@ func (s *HistogramBucket) Equal(o *HistogramBucket) bool {
|
||||
return s == o || (s.Boundaries == o.Boundaries && s.Lower == o.Lower && s.Upper == o.Upper && s.Count == o.Count)
|
||||
}
|
||||
|
||||
func (b HistogramBucket) String() string {
|
||||
func (s HistogramBucket) String() string {
|
||||
var sb strings.Builder
|
||||
lowerInclusive := b.Boundaries == 1 || b.Boundaries == 3
|
||||
upperInclusive := b.Boundaries == 0 || b.Boundaries == 3
|
||||
lowerInclusive := s.Boundaries == 1 || s.Boundaries == 3
|
||||
upperInclusive := s.Boundaries == 0 || s.Boundaries == 3
|
||||
if lowerInclusive {
|
||||
sb.WriteRune('[')
|
||||
} else {
|
||||
sb.WriteRune('(')
|
||||
}
|
||||
fmt.Fprintf(&sb, "%g,%g", b.Lower, b.Upper)
|
||||
fmt.Fprintf(&sb, "%g,%g", s.Lower, s.Upper)
|
||||
if upperInclusive {
|
||||
sb.WriteRune(']')
|
||||
} else {
|
||||
sb.WriteRune(')')
|
||||
}
|
||||
fmt.Fprintf(&sb, ":%v", b.Count)
|
||||
fmt.Fprintf(&sb, ":%v", s.Count)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
@@ -141,7 +142,7 @@ type SampleHistogramPair struct {
|
||||
|
||||
func (s SampleHistogramPair) MarshalJSON() ([]byte, error) {
|
||||
if s.Histogram == nil {
|
||||
return nil, fmt.Errorf("histogram is nil")
|
||||
return nil, errors.New("histogram is nil")
|
||||
}
|
||||
t, err := json.Marshal(s.Timestamp)
|
||||
if err != nil {
|
||||
@@ -164,7 +165,7 @@ func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error {
|
||||
return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen)
|
||||
}
|
||||
if s.Histogram == nil {
|
||||
return fmt.Errorf("histogram is null")
|
||||
return errors.New("histogram is null")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
4
vendor/github.com/prometheus/common/model/value_type.go
generated
vendored
4
vendor/github.com/prometheus/common/model/value_type.go
generated
vendored
@@ -66,8 +66,8 @@ func (et *ValueType) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e ValueType) String() string {
|
||||
switch e {
|
||||
func (et ValueType) String() string {
|
||||
switch et {
|
||||
case ValNone:
|
||||
return "<ValNone>"
|
||||
case ValScalar:
|
||||
|
||||
Reference in New Issue
Block a user