diff --git a/extraction/metricfamilyprocessor.go b/extraction/metricfamilyprocessor.go index 5ed5343..608e786 100644 --- a/extraction/metricfamilyprocessor.go +++ b/extraction/metricfamilyprocessor.go @@ -16,6 +16,7 @@ package extraction import ( "fmt" "io" + "math" dto "github.com/prometheus/client_model/go" @@ -85,7 +86,10 @@ func extractCounter(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error continue } - sample := new(model.Sample) + sample := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Counter.GetValue()), + } samples = append(samples, sample) if m.TimestampMs != nil { @@ -93,16 +97,12 @@ func extractCounter(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error } else { sample.Timestamp = o.Timestamp } - sample.Metric = model.Metric{} - metric := sample.Metric + metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - - sample.Value = model.SampleValue(m.Counter.GetValue()) } return out.Ingest(samples) @@ -116,7 +116,10 @@ func extractGauge(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { continue } - sample := new(model.Sample) + sample := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Gauge.GetValue()), + } samples = append(samples, sample) if m.TimestampMs != nil { @@ -124,16 +127,12 @@ func extractGauge(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { } else { sample.Timestamp = o.Timestamp } - sample.Metric = model.Metric{} - metric := sample.Metric + metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - - sample.Value = model.SampleValue(m.Gauge.GetValue()) } return out.Ingest(samples) @@ -153,48 +152,50 @@ func extractSummary(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error } for _, q := range m.Summary.Quantile { - sample := new(model.Sample) + sample := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(q.GetValue()), + Timestamp: timestamp, + } samples = append(samples, sample) - sample.Timestamp = timestamp - sample.Metric = model.Metric{} metric := sample.Metric - for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } // BUG(matt): Update other names to "quantile". metric[model.LabelName("quantile")] = model.LabelValue(fmt.Sprint(q.GetQuantile())) - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - - sample.Value = model.SampleValue(q.GetValue()) } if m.Summary.SampleSum != nil { - sum := new(model.Sample) - sum.Timestamp = timestamp - metric := model.Metric{} + sum := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Summary.GetSampleSum()), + Timestamp: timestamp, + } + samples = append(samples, sum) + + metric := sum.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") - sum.Metric = metric - sum.Value = model.SampleValue(m.Summary.GetSampleSum()) - samples = append(samples, sum) } if m.Summary.SampleCount != nil { - count := new(model.Sample) - count.Timestamp = timestamp - metric := model.Metric{} + count := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Summary.GetSampleCount()), + Timestamp: timestamp, + } + samples = append(samples, count) + + metric := count.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") - count.Metric = metric - count.Value = model.SampleValue(m.Summary.GetSampleCount()) - samples = append(samples, count) } } @@ -209,7 +210,10 @@ func extractUntyped(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error continue } - sample := new(model.Sample) + sample := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Untyped.GetValue()), + } samples = append(samples, sample) if m.TimestampMs != nil { @@ -217,16 +221,12 @@ func extractUntyped(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error } else { sample.Timestamp = o.Timestamp } - sample.Metric = model.Metric{} - metric := sample.Metric + metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } - metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) - - sample.Value = model.SampleValue(m.Untyped.GetValue()) } return out.Ingest(samples) @@ -245,49 +245,72 @@ func extractHistogram(out Ingester, o *ProcessOptions, f *dto.MetricFamily) erro timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) } + infSeen := false + for _, q := range m.Histogram.Bucket { - sample := new(model.Sample) + sample := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(q.GetCumulativeCount()), + Timestamp: timestamp, + } samples = append(samples, sample) - sample.Timestamp = timestamp - sample.Metric = model.Metric{} metric := sample.Metric - for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.LabelName("le")] = model.LabelValue(fmt.Sprint(q.GetUpperBound())) - metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") - sample.Value = model.SampleValue(q.GetCumulativeCount()) + if math.IsInf(q.GetUpperBound(), +1) { + infSeen = true + } } - // TODO: If +Inf bucket is missing, add it. if m.Histogram.SampleSum != nil { - sum := new(model.Sample) - sum.Timestamp = timestamp - metric := model.Metric{} + sum := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Histogram.GetSampleSum()), + Timestamp: timestamp, + } + samples = append(samples, sum) + + metric := sum.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") - sum.Metric = metric - sum.Value = model.SampleValue(m.Histogram.GetSampleSum()) - samples = append(samples, sum) } if m.Histogram.SampleCount != nil { - count := new(model.Sample) - count.Timestamp = timestamp - metric := model.Metric{} + count := &model.Sample{ + Metric: model.Metric{}, + Value: model.SampleValue(m.Histogram.GetSampleCount()), + Timestamp: timestamp, + } + samples = append(samples, count) + + metric := count.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") - count.Metric = metric - count.Value = model.SampleValue(m.Histogram.GetSampleCount()) - samples = append(samples, count) + + if !infSeen { + infBucket := &model.Sample{ + Metric: model.Metric{}, + Value: count.Value, + Timestamp: timestamp, + } + samples = append(samples, infBucket) + + metric := infBucket.Metric + for _, p := range m.Label { + metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) + } + metric[model.LabelName("le")] = model.LabelValue("+Inf") + metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") + } } } diff --git a/text/create.go b/text/create.go index 64e372c..1b1cbd3 100644 --- a/text/create.go +++ b/text/create.go @@ -24,6 +24,7 @@ import ( "bytes" "fmt" "io" + "math" "strings" dto "github.com/prometheus/client_model/go" @@ -145,6 +146,7 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { "expected summary in metric %s", metric, ) } + infSeen := false for _, q := range metric.Histogram.Bucket { n, err = writeSample( name+"_bucket", metric, @@ -156,7 +158,21 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { if err != nil { return written, err } - // TODO: Add +inf bucket if it's missing. + if math.IsInf(q.GetUpperBound(), +1) { + infSeen = true + } + } + if !infSeen { + n, err = writeSample( + name+"_bucket", metric, + "le", "+Inf", + float64(metric.Histogram.GetSampleCount()), + out, + ) + if err != nil { + return written, err + } + written += n } n, err = writeSample( name+"_sum", metric, "", "", diff --git a/text/create_test.go b/text/create_test.go index 9326e6e..fe938de 100644 --- a/text/create_test.go +++ b/text/create_test.go @@ -267,6 +267,50 @@ request_duration_microseconds_bucket{le="172.8"} 1524 request_duration_microseconds_bucket{le="+Inf"} 2693 request_duration_microseconds_sum 1.7560473e+06 request_duration_microseconds_count 2693 +`, + }, + // 5: Histogram with missing +Inf bucket. + { + in: &dto.MetricFamily{ + Name: proto.String("request_duration_microseconds"), + Help: proto.String("The response latency."), + Type: dto.MetricType_HISTOGRAM.Enum(), + Metric: []*dto.Metric{ + &dto.Metric{ + Histogram: &dto.Histogram{ + SampleCount: proto.Uint64(2693), + SampleSum: proto.Float64(1756047.3), + Bucket: []*dto.Bucket{ + &dto.Bucket{ + UpperBound: proto.Float64(100), + CumulativeCount: proto.Uint64(123), + }, + &dto.Bucket{ + UpperBound: proto.Float64(120), + CumulativeCount: proto.Uint64(412), + }, + &dto.Bucket{ + UpperBound: proto.Float64(144), + CumulativeCount: proto.Uint64(592), + }, + &dto.Bucket{ + UpperBound: proto.Float64(172.8), + CumulativeCount: proto.Uint64(1524), + }, + }, + }, + }, + }, + }, + out: `# HELP request_duration_microseconds The response latency. +# TYPE request_duration_microseconds histogram +request_duration_microseconds_bucket{le="100"} 123 +request_duration_microseconds_bucket{le="120"} 412 +request_duration_microseconds_bucket{le="144"} 592 +request_duration_microseconds_bucket{le="172.8"} 1524 +request_duration_microseconds_bucket{le="+Inf"} 2693 +request_duration_microseconds_sum 1.7560473e+06 +request_duration_microseconds_count 2693 `, }, }