diff --git a/web/federate.go b/web/federate.go index 5e243769b9..34da550a82 100644 --- a/web/federate.go +++ b/web/federate.go @@ -190,10 +190,13 @@ Loop: isHistogram := s.H != nil formatType := format.FormatType() if isHistogram && + !s.H.UsesCustomBuckets() && formatType != expfmt.TypeProtoDelim && formatType != expfmt.TypeProtoText && formatType != expfmt.TypeProtoCompact { - // Can't serve the native histogram. + // Can't serve a native histogram with a non-protobuf format. + // (We can serve an NHCB, though, as it is converted to a + // classic histogram for federation.) // TODO(codesome): Serve them when other protocols get the native histogram support. continue } @@ -290,32 +293,10 @@ Loop: } } else { lastHistogramWasGauge = s.H.CounterResetHint == histogram.GaugeType - protMetric.Histogram = &dto.Histogram{ - SampleCountFloat: proto.Float64(s.H.Count), - SampleSum: proto.Float64(s.H.Sum), - Schema: proto.Int32(s.H.Schema), - ZeroThreshold: proto.Float64(s.H.ZeroThreshold), - ZeroCountFloat: proto.Float64(s.H.ZeroCount), - NegativeCount: s.H.NegativeBuckets, - PositiveCount: s.H.PositiveBuckets, - } - if len(s.H.PositiveSpans) > 0 { - protMetric.Histogram.PositiveSpan = make([]*dto.BucketSpan, len(s.H.PositiveSpans)) - for i, sp := range s.H.PositiveSpans { - protMetric.Histogram.PositiveSpan[i] = &dto.BucketSpan{ - Offset: proto.Int32(sp.Offset), - Length: proto.Uint32(sp.Length), - } - } - } - if len(s.H.NegativeSpans) > 0 { - protMetric.Histogram.NegativeSpan = make([]*dto.BucketSpan, len(s.H.NegativeSpans)) - for i, sp := range s.H.NegativeSpans { - protMetric.Histogram.NegativeSpan[i] = &dto.BucketSpan{ - Offset: proto.Int32(sp.Offset), - Length: proto.Uint32(sp.Length), - } - } + if s.H.UsesCustomBuckets() { + protMetric.Histogram = makeClassicHistogram(s.H) + } else { + protMetric.Histogram = makeNativeHistogram(s.H) } } lastWasHistogram = isHistogram @@ -329,3 +310,66 @@ Loop: } } } + +// makeNativeHistogram creates a dto.Histogram representing a native histogram. +// Use only for standard exponential schemal. +func makeNativeHistogram(h *histogram.FloatHistogram) *dto.Histogram { + result := &dto.Histogram{ + SampleCountFloat: proto.Float64(h.Count), + SampleSum: proto.Float64(h.Sum), + Schema: proto.Int32(h.Schema), + ZeroThreshold: proto.Float64(h.ZeroThreshold), + ZeroCountFloat: proto.Float64(h.ZeroCount), + NegativeCount: h.NegativeBuckets, + PositiveCount: h.PositiveBuckets, + } + if len(h.PositiveSpans) > 0 { + result.PositiveSpan = make([]*dto.BucketSpan, len(h.PositiveSpans)) + for i, sp := range h.PositiveSpans { + result.PositiveSpan[i] = &dto.BucketSpan{ + Offset: proto.Int32(sp.Offset), + Length: proto.Uint32(sp.Length), + } + } + } + if len(h.NegativeSpans) > 0 { + result.NegativeSpan = make([]*dto.BucketSpan, len(h.NegativeSpans)) + for i, sp := range h.NegativeSpans { + result.NegativeSpan[i] = &dto.BucketSpan{ + Offset: proto.Int32(sp.Offset), + Length: proto.Uint32(sp.Length), + } + } + } + return result +} + +// makeClassicHistogram creates a dto.Histogram representing a classic +// histogram. Use only for NHCB (schema -53). +func makeClassicHistogram(h *histogram.FloatHistogram) *dto.Histogram { + result := &dto.Histogram{ + SampleCountFloat: proto.Float64(h.Count), + SampleSum: proto.Float64(h.Sum), + } + result.Bucket = make([]*dto.Bucket, len(h.CustomValues)) + var ( + cumulativeCount float64 + bucketIter = h.PositiveBucketIterator() + bucketAvailable = bucketIter.Next() + ) + for i, le := range h.CustomValues { + for bucketAvailable && int(bucketIter.At().Index) < i { + bucketAvailable = bucketIter.Next() + } + if bucketAvailable && int(bucketIter.At().Index) == i { + cumulativeCount += bucketIter.At().Count + } + result.Bucket[i] = &dto.Bucket{ + UpperBound: proto.Float64(le), + CumulativeCountFloat: proto.Float64(cumulativeCount), + } + } + // Note that we do not add the +Inf bucket explicitly. In the protobuf + // exposition format, it is optional. + return result +}