Skip to content

Commit 92219ee

Browse files
committed
expfmt: Add support for float histograms and gauge histograms
Signed-off-by: beorn7 <beorn@grafana.com>
1 parent 0df7b91 commit 92219ee

7 files changed

Lines changed: 782 additions & 56 deletions

File tree

expfmt/decode.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ func extractSamples(f *dto.MetricFamily, o *DecodeOptions) (model.Vector, error)
220220
return extractSummary(o, f), nil
221221
case dto.MetricType_UNTYPED:
222222
return extractUntyped(o, f), nil
223-
case dto.MetricType_HISTOGRAM:
223+
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
224224
return extractHistogram(o, f), nil
225225
}
226226
return nil, fmt.Errorf("expfmt.extractSamples: unknown metric family type %v", f.GetType())
@@ -403,9 +403,13 @@ func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
403403
infSeen = true
404404
}
405405

406+
v := q.GetCumulativeCountFloat()
407+
if v <= 0 {
408+
v = float64(q.GetCumulativeCount())
409+
}
406410
samples = append(samples, &model.Sample{
407411
Metric: model.Metric(lset),
408-
Value: model.SampleValue(q.GetCumulativeCount()),
412+
Value: model.SampleValue(v),
409413
Timestamp: timestamp,
410414
})
411415
}
@@ -428,9 +432,13 @@ func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
428432
}
429433
lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
430434

435+
v := m.Histogram.GetSampleCountFloat()
436+
if v <= 0 {
437+
v = float64(m.Histogram.GetSampleCount())
438+
}
431439
count := &model.Sample{
432440
Metric: model.Metric(lset),
433-
Value: model.SampleValue(m.Histogram.GetSampleCount()),
441+
Value: model.SampleValue(v),
434442
Timestamp: timestamp,
435443
}
436444
samples = append(samples, count)

expfmt/openmetrics_create.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E
208208
n, err = w.WriteString(" unknown\n")
209209
case dto.MetricType_HISTOGRAM:
210210
n, err = w.WriteString(" histogram\n")
211+
case dto.MetricType_GAUGE_HISTOGRAM:
212+
n, err = w.WriteString(" gaugehistogram\n")
211213
default:
212214
return written, fmt.Errorf("unknown metric type %s", metricType.String())
213215
}
@@ -325,14 +327,20 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E
325327
createdTsBytesWritten, err = writeOpenMetricsCreated(w, compliantName, "", metric, "", 0, metric.Summary.GetCreatedTimestamp())
326328
n += createdTsBytesWritten
327329
}
328-
case dto.MetricType_HISTOGRAM:
330+
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
329331
if metric.Histogram == nil {
330332
return written, fmt.Errorf(
331333
"expected histogram in metric %s %s", compliantName, metric,
332334
)
333335
}
334336
infSeen := false
335337
for _, b := range metric.Histogram.Bucket {
338+
if b.GetCumulativeCountFloat() > 0 {
339+
return written, fmt.Errorf(
340+
"float histogram %s %s not supported in OpenMetrics",
341+
compliantName, metric,
342+
)
343+
}
336344
n, err = writeOpenMetricsSample(
337345
w, compliantName, "_bucket", metric,
338346
model.BucketLabel, b.GetUpperBound(),
@@ -354,6 +362,9 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E
354362
0, metric.Histogram.GetSampleCount(), true,
355363
nil,
356364
)
365+
// We do not check for a float sample count here
366+
// because we will check for it below (and error
367+
// out if needed).
357368
written += n
358369
if err != nil {
359370
return
@@ -368,6 +379,12 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E
368379
if err != nil {
369380
return
370381
}
382+
if metric.Histogram.GetSampleCountFloat() > 0 {
383+
return written, fmt.Errorf(
384+
"float histogram %s %s not supported in OpenMetrics",
385+
compliantName, metric,
386+
)
387+
}
371388
n, err = writeOpenMetricsSample(
372389
w, compliantName, "_count", metric, "", 0,
373390
0, metric.Histogram.GetSampleCount(), true,

expfmt/openmetrics_create_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,50 @@ request_duration_microseconds_count 2693
743743
# UNIT some_measure_seconds seconds
744744
some_measure_seconds_total{labelname="val1",basename="basevalue"} 42.0
745745
some_measure_seconds_total{labelname="val2",basename="basevalue"} 0.23 1.23456789e+06
746+
`,
747+
},
748+
// 11: Gauge histogram.
749+
{
750+
in: &dto.MetricFamily{
751+
Name: proto.String("name"),
752+
Help: proto.String("doc string"),
753+
Type: dto.MetricType_GAUGE_HISTOGRAM.Enum(),
754+
Metric: []*dto.Metric{
755+
{
756+
Histogram: &dto.Histogram{
757+
SampleCount: proto.Uint64(2693),
758+
SampleSum: proto.Float64(1756047.3),
759+
Bucket: []*dto.Bucket{
760+
{
761+
UpperBound: proto.Float64(100),
762+
CumulativeCount: proto.Uint64(123),
763+
},
764+
{
765+
UpperBound: proto.Float64(120),
766+
CumulativeCount: proto.Uint64(412),
767+
},
768+
{
769+
UpperBound: proto.Float64(144),
770+
CumulativeCount: proto.Uint64(592),
771+
},
772+
{
773+
UpperBound: proto.Float64(172.8),
774+
CumulativeCount: proto.Uint64(1524),
775+
},
776+
},
777+
},
778+
},
779+
},
780+
},
781+
out: `# HELP name doc string
782+
# TYPE name gaugehistogram
783+
name_bucket{le="100.0"} 123
784+
name_bucket{le="120.0"} 412
785+
name_bucket{le="144.0"} 592
786+
name_bucket{le="172.8"} 1524
787+
name_bucket{le="+Inf"} 2693
788+
name_sum 1.7560473e+06
789+
name_count 2693
746790
`,
747791
},
748792
}
@@ -903,6 +947,47 @@ func TestOpenMetricsCreateError(t *testing.T) {
903947
},
904948
err: "expected counter in metric",
905949
},
950+
// 2: Float histogram.
951+
{
952+
in: &dto.MetricFamily{
953+
Name: proto.String("name"),
954+
Help: proto.String("doc string"),
955+
Type: dto.MetricType_GAUGE_HISTOGRAM.Enum(),
956+
Metric: []*dto.Metric{
957+
{
958+
Histogram: &dto.Histogram{
959+
// Note that it is enough to fill the float fields even
960+
// if the values are integers.
961+
SampleCountFloat: proto.Float64(2693),
962+
SampleSum: proto.Float64(1756047.3),
963+
Bucket: []*dto.Bucket{
964+
{
965+
UpperBound: proto.Float64(100),
966+
CumulativeCountFloat: proto.Float64(123),
967+
},
968+
{
969+
UpperBound: proto.Float64(120),
970+
CumulativeCountFloat: proto.Float64(412),
971+
},
972+
{
973+
UpperBound: proto.Float64(144),
974+
CumulativeCountFloat: proto.Float64(592),
975+
},
976+
{
977+
UpperBound: proto.Float64(172.8),
978+
CumulativeCountFloat: proto.Float64(1524),
979+
},
980+
{
981+
UpperBound: proto.Float64(math.Inf(+1)),
982+
CumulativeCountFloat: proto.Float64(2693),
983+
},
984+
},
985+
},
986+
},
987+
},
988+
},
989+
err: "float histogram name histogram",
990+
},
906991
}
907992

908993
for i, scenario := range scenarios {

expfmt/text_create.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
151151
n, err = w.WriteString(" summary\n")
152152
case dto.MetricType_UNTYPED:
153153
n, err = w.WriteString(" untyped\n")
154-
case dto.MetricType_HISTOGRAM:
154+
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
155+
// The classic Prometheus text format has no notion of a gauge
156+
// histogram. We render a gauge histogram in the same way as a
157+
// regular histogram.
155158
n, err = w.WriteString(" histogram\n")
156159
default:
157160
return written, fmt.Errorf("unknown metric type %s", metricType.String())
@@ -223,18 +226,22 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
223226
w, name, "_count", metric, "", 0,
224227
float64(metric.Summary.GetSampleCount()),
225228
)
226-
case dto.MetricType_HISTOGRAM:
229+
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
227230
if metric.Histogram == nil {
228231
return written, fmt.Errorf(
229232
"expected histogram in metric %s %s", name, metric,
230233
)
231234
}
232235
infSeen := false
233236
for _, b := range metric.Histogram.Bucket {
237+
v := b.GetCumulativeCountFloat()
238+
if v == 0 {
239+
v = float64(b.GetCumulativeCount())
240+
}
234241
n, err = writeSample(
235242
w, name, "_bucket", metric,
236243
model.BucketLabel, b.GetUpperBound(),
237-
float64(b.GetCumulativeCount()),
244+
v,
238245
)
239246
written += n
240247
if err != nil {
@@ -245,10 +252,14 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
245252
}
246253
}
247254
if !infSeen {
255+
v := metric.Histogram.GetSampleCountFloat()
256+
if v == 0 {
257+
v = float64(metric.Histogram.GetSampleCount())
258+
}
248259
n, err = writeSample(
249260
w, name, "_bucket", metric,
250261
model.BucketLabel, math.Inf(+1),
251-
float64(metric.Histogram.GetSampleCount()),
262+
v,
252263
)
253264
written += n
254265
if err != nil {
@@ -263,10 +274,11 @@ func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err e
263274
if err != nil {
264275
return
265276
}
266-
n, err = writeSample(
267-
w, name, "_count", metric, "", 0,
268-
float64(metric.Histogram.GetSampleCount()),
269-
)
277+
v := metric.Histogram.GetSampleCountFloat()
278+
if v == 0 {
279+
v = float64(metric.Histogram.GetSampleCount())
280+
}
281+
n, err = writeSample(w, name, "_count", metric, "", 0, v)
270282
default:
271283
return written, fmt.Errorf(
272284
"unexpected type in metric %s %s", name, metric,

0 commit comments

Comments
 (0)