Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
394
plugins/parsers/openmetrics/textparse.go
Normal file
394
plugins/parsers/openmetrics/textparse.go
Normal file
|
@ -0,0 +1,394 @@
|
|||
package openmetrics
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/maphash"
|
||||
"io"
|
||||
"math"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/model/textparse"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
func TextToMetricFamilies(data []byte) ([]*MetricFamily, error) {
|
||||
var metrics []*MetricFamily
|
||||
|
||||
parser := textparse.NewOpenMetricsParser(data, nil)
|
||||
|
||||
seed := maphash.MakeSeed()
|
||||
mf := &MetricFamily{}
|
||||
mfMetric := &Metric{}
|
||||
mfMetricKey := uint64(0)
|
||||
mfMetricPoint := &MetricPoint{}
|
||||
for {
|
||||
entry, err := parser.Next()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
if mf.Name != "" {
|
||||
if mfMetricPoint.Value != nil {
|
||||
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||
}
|
||||
if len(mfMetric.MetricPoints) > 0 {
|
||||
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||
}
|
||||
metrics = append(metrics, mf)
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("parsing failed: %w", err)
|
||||
}
|
||||
|
||||
switch entry {
|
||||
case textparse.EntryInvalid:
|
||||
continue
|
||||
case textparse.EntryType:
|
||||
name, mtype := parser.Type()
|
||||
if len(name) == 0 {
|
||||
return nil, errors.New("empty metric-family name")
|
||||
}
|
||||
|
||||
if mf.Name == "" {
|
||||
mf.Name = string(name)
|
||||
} else if mf.Name != string(name) {
|
||||
if mfMetricPoint.Value != nil {
|
||||
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||
}
|
||||
if len(mfMetric.MetricPoints) > 0 {
|
||||
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||
}
|
||||
metrics = append(metrics, mf)
|
||||
mf = &MetricFamily{Name: string(name)}
|
||||
mfMetric = &Metric{}
|
||||
mfMetricKey = 0
|
||||
mfMetricPoint = &MetricPoint{}
|
||||
}
|
||||
|
||||
switch mtype {
|
||||
case model.MetricTypeCounter:
|
||||
mf.Type = MetricType_COUNTER
|
||||
case model.MetricTypeGauge:
|
||||
mf.Type = MetricType_GAUGE
|
||||
case model.MetricTypeHistogram:
|
||||
mf.Type = MetricType_HISTOGRAM
|
||||
case model.MetricTypeGaugeHistogram:
|
||||
mf.Type = MetricType_GAUGE_HISTOGRAM
|
||||
case model.MetricTypeSummary:
|
||||
mf.Type = MetricType_SUMMARY
|
||||
case model.MetricTypeInfo:
|
||||
mf.Type = MetricType_INFO
|
||||
case model.MetricTypeStateset:
|
||||
mf.Type = MetricType_STATE_SET
|
||||
case model.MetricTypeUnknown:
|
||||
mf.Type = MetricType_UNKNOWN
|
||||
}
|
||||
case textparse.EntryHelp:
|
||||
name, mhelp := parser.Help()
|
||||
if len(name) == 0 {
|
||||
return nil, errors.New("empty metric-family name")
|
||||
}
|
||||
|
||||
if mf.Name == "" {
|
||||
mf.Name = string(name)
|
||||
} else if mf.Name != string(name) {
|
||||
if mfMetricPoint.Value != nil {
|
||||
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||
}
|
||||
if len(mfMetric.MetricPoints) > 0 {
|
||||
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||
}
|
||||
metrics = append(metrics, mf)
|
||||
mf = &MetricFamily{Name: string(name)}
|
||||
mfMetric = &Metric{}
|
||||
mfMetricKey = 0
|
||||
mfMetricPoint = &MetricPoint{}
|
||||
}
|
||||
mf.Help = string(mhelp)
|
||||
case textparse.EntrySeries:
|
||||
series, ts, value := parser.Series()
|
||||
|
||||
// Extract the metric name and labels
|
||||
dn, _, _ := bytes.Cut(series, []byte("{"))
|
||||
if len(dn) == 0 {
|
||||
return nil, errors.New("empty metric name")
|
||||
}
|
||||
sampleName := string(dn)
|
||||
|
||||
var metricLabels labels.Labels
|
||||
parser.Metric(&metricLabels)
|
||||
|
||||
// There might be metrics without meta-information, however in this
|
||||
// case the metric is of type UNKNOWN according to the spec and do
|
||||
// only contain a single metric. Therefore, we can use the metric
|
||||
// name as metric-family name
|
||||
if mf.Name == "" {
|
||||
mf.Name = sampleName
|
||||
}
|
||||
|
||||
// The name contained in the sample is constructed using the metric
|
||||
// name and an optional sample-type suffix used for more complex
|
||||
// types (e.g. histograms).
|
||||
sampleType, seriesLabels := extractSampleType(sampleName, mf.Name, mf.Type, &metricLabels)
|
||||
|
||||
// Check if we are still in the same metric, if not, add the
|
||||
// previous one to the metric family and create a new one...
|
||||
key := getSeriesKey(seriesLabels, seed)
|
||||
if mfMetricKey != key {
|
||||
if mfMetricPoint.Value != nil {
|
||||
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||
}
|
||||
if len(mfMetric.MetricPoints) > 0 {
|
||||
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||
}
|
||||
mfMetric = &Metric{}
|
||||
mfMetricKey = key
|
||||
mfMetricPoint = &MetricPoint{}
|
||||
mfMetric.Labels = make([]*Label, 0, len(*seriesLabels))
|
||||
for _, l := range *seriesLabels {
|
||||
mfMetric.Labels = append(mfMetric.Labels, &Label{
|
||||
Name: l.Name,
|
||||
Value: l.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we are still in the same metric-point
|
||||
var mpTimestamp int64
|
||||
if mfMetricPoint.Timestamp != nil {
|
||||
mpTimestamp = mfMetricPoint.Timestamp.Seconds * int64(time.Second)
|
||||
mpTimestamp += int64(mfMetricPoint.Timestamp.Nanos)
|
||||
}
|
||||
var timestamp int64
|
||||
if ts != nil {
|
||||
timestamp = *ts * int64(time.Millisecond)
|
||||
}
|
||||
if mpTimestamp != timestamp {
|
||||
if mfMetricPoint.Value != nil {
|
||||
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||
}
|
||||
mfMetricPoint = &MetricPoint{}
|
||||
if ts != nil {
|
||||
mfMetricPoint.Timestamp = timestamppb.New(time.Unix(0, timestamp))
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in the metric-point
|
||||
mfMetricPoint.set(mf.Name, mf.Type, sampleType, value, &metricLabels)
|
||||
case textparse.EntryComment:
|
||||
// ignore comments
|
||||
case textparse.EntryUnit:
|
||||
name, munit := parser.Unit()
|
||||
if len(name) == 0 {
|
||||
return nil, errors.New("empty metric-family name")
|
||||
}
|
||||
|
||||
if mf.Name == "" {
|
||||
mf.Name = string(name)
|
||||
} else if mf.Name != string(name) {
|
||||
if mfMetricPoint.Value != nil {
|
||||
mfMetric.MetricPoints = append(mfMetric.MetricPoints, mfMetricPoint)
|
||||
}
|
||||
if len(mfMetric.MetricPoints) > 0 {
|
||||
mf.Metrics = append(mf.Metrics, mfMetric)
|
||||
}
|
||||
metrics = append(metrics, mf)
|
||||
mf = &MetricFamily{Name: string(name)}
|
||||
mfMetric = &Metric{}
|
||||
mfMetricKey = 0
|
||||
mfMetricPoint = &MetricPoint{}
|
||||
}
|
||||
mf.Unit = string(munit)
|
||||
case textparse.EntryHistogram:
|
||||
// not supported yet
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown entry type %v", entry)
|
||||
}
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func getSeriesKey(seriesLabels *labels.Labels, seed maphash.Seed) uint64 {
|
||||
sorted := make([]string, 0, len(*seriesLabels))
|
||||
for _, l := range *seriesLabels {
|
||||
sorted = append(sorted, l.Name+"="+l.Value)
|
||||
}
|
||||
slices.Sort(sorted)
|
||||
|
||||
var h maphash.Hash
|
||||
h.SetSeed(seed)
|
||||
for _, p := range sorted {
|
||||
h.WriteString(p)
|
||||
h.WriteByte(0)
|
||||
}
|
||||
return h.Sum64()
|
||||
}
|
||||
|
||||
func extractSampleType(raw, name string, mtype MetricType, metricLabels *labels.Labels) (string, *labels.Labels) {
|
||||
suffix := strings.TrimLeft(strings.TrimPrefix(raw, name), "_")
|
||||
var seriesLabels labels.Labels
|
||||
for _, l := range *metricLabels {
|
||||
// filter out special labels
|
||||
switch {
|
||||
case l.Name == "__name__":
|
||||
case mtype == MetricType_STATE_SET && l.Name == name:
|
||||
case mtype == MetricType_HISTOGRAM && l.Name == "le":
|
||||
case mtype == MetricType_GAUGE_HISTOGRAM && l.Name == "le":
|
||||
case mtype == MetricType_SUMMARY && l.Name == "quantile":
|
||||
default:
|
||||
seriesLabels = append(seriesLabels, labels.Label{Name: l.Name, Value: l.Value})
|
||||
}
|
||||
}
|
||||
return suffix, &seriesLabels
|
||||
}
|
||||
|
||||
func (mp *MetricPoint) set(mname string, mtype MetricType, stype string, value float64, mlabels *labels.Labels) {
|
||||
switch mtype {
|
||||
case MetricType_UNKNOWN:
|
||||
mp.Value = &MetricPoint_UnknownValue{
|
||||
UnknownValue: &UnknownValue{
|
||||
Value: &UnknownValue_DoubleValue{DoubleValue: value},
|
||||
},
|
||||
}
|
||||
case MetricType_GAUGE:
|
||||
mp.Value = &MetricPoint_GaugeValue{
|
||||
GaugeValue: &GaugeValue{
|
||||
Value: &GaugeValue_DoubleValue{DoubleValue: value},
|
||||
},
|
||||
}
|
||||
case MetricType_COUNTER:
|
||||
var v *MetricPoint_CounterValue
|
||||
if mp.Value != nil {
|
||||
v = mp.Value.(*MetricPoint_CounterValue)
|
||||
} else {
|
||||
v = &MetricPoint_CounterValue{
|
||||
CounterValue: &CounterValue{},
|
||||
}
|
||||
}
|
||||
switch stype {
|
||||
case "total":
|
||||
v.CounterValue.Total = &CounterValue_DoubleValue{DoubleValue: value}
|
||||
case "created":
|
||||
t := time.Unix(0, int64(value*float64(time.Second)))
|
||||
v.CounterValue.Created = timestamppb.New(t)
|
||||
}
|
||||
mp.Value = v
|
||||
case MetricType_STATE_SET:
|
||||
var v *MetricPoint_StateSetValue
|
||||
if mp.Value != nil {
|
||||
v = mp.Value.(*MetricPoint_StateSetValue)
|
||||
} else {
|
||||
v = &MetricPoint_StateSetValue{
|
||||
StateSetValue: &StateSetValue{},
|
||||
}
|
||||
}
|
||||
|
||||
var name string
|
||||
for _, l := range *mlabels {
|
||||
if l.Name == mname {
|
||||
name = l.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
v.StateSetValue.States = append(v.StateSetValue.States, &StateSetValue_State{
|
||||
Enabled: value > 0,
|
||||
Name: name,
|
||||
})
|
||||
mp.Value = v
|
||||
case MetricType_INFO:
|
||||
mp.Value = &MetricPoint_InfoValue{
|
||||
InfoValue: &InfoValue{},
|
||||
}
|
||||
case MetricType_HISTOGRAM, MetricType_GAUGE_HISTOGRAM:
|
||||
var v *MetricPoint_HistogramValue
|
||||
if mp.Value != nil {
|
||||
v = mp.Value.(*MetricPoint_HistogramValue)
|
||||
} else {
|
||||
v = &MetricPoint_HistogramValue{
|
||||
HistogramValue: &HistogramValue{},
|
||||
}
|
||||
}
|
||||
|
||||
switch stype {
|
||||
case "sum", "gsum":
|
||||
v.HistogramValue.Sum = &HistogramValue_DoubleValue{DoubleValue: value}
|
||||
case "count", "gcount":
|
||||
v.HistogramValue.Count = uint64(value)
|
||||
case "created":
|
||||
t := time.Unix(0, int64(value*float64(time.Second)))
|
||||
v.HistogramValue.Created = timestamppb.New(t)
|
||||
case "bucket":
|
||||
var boundLabel string
|
||||
for _, l := range *mlabels {
|
||||
if l.Name == "le" {
|
||||
boundLabel = l.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
var bound float64
|
||||
if boundLabel == "+Inf" {
|
||||
bound = math.Inf(1)
|
||||
} else {
|
||||
var err error
|
||||
if bound, err = strconv.ParseFloat(boundLabel, 64); err != nil {
|
||||
bound = math.NaN()
|
||||
}
|
||||
}
|
||||
|
||||
v.HistogramValue.Buckets = append(v.HistogramValue.Buckets, &HistogramValue_Bucket{
|
||||
Count: uint64(value),
|
||||
UpperBound: bound,
|
||||
})
|
||||
}
|
||||
mp.Value = v
|
||||
case MetricType_SUMMARY:
|
||||
var v *MetricPoint_SummaryValue
|
||||
if mp.Value != nil {
|
||||
v = mp.Value.(*MetricPoint_SummaryValue)
|
||||
} else {
|
||||
v = &MetricPoint_SummaryValue{
|
||||
SummaryValue: &SummaryValue{},
|
||||
}
|
||||
}
|
||||
|
||||
switch stype {
|
||||
case "sum":
|
||||
v.SummaryValue.Sum = &SummaryValue_DoubleValue{DoubleValue: value}
|
||||
case "count":
|
||||
v.SummaryValue.Count = uint64(value)
|
||||
case "created":
|
||||
t := time.Unix(0, int64(value*float64(time.Second)))
|
||||
v.SummaryValue.Created = timestamppb.New(t)
|
||||
default:
|
||||
var quantileLabel string
|
||||
for _, l := range *mlabels {
|
||||
if l.Name == "quantile" {
|
||||
quantileLabel = l.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
var quantile float64
|
||||
if quantileLabel == "+Inf" {
|
||||
quantile = math.MaxFloat64
|
||||
} else {
|
||||
var err error
|
||||
if quantile, err = strconv.ParseFloat(quantileLabel, 64); err != nil {
|
||||
quantile = math.NaN()
|
||||
}
|
||||
}
|
||||
|
||||
v.SummaryValue.Quantile = append(v.SummaryValue.Quantile, &SummaryValue_Quantile{
|
||||
Quantile: quantile,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
mp.Value = v
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue