From a51096f4019ca4ceac32614d5dd123a86c1d2a30 Mon Sep 17 00:00:00 2001 From: Damien Grisonnet Date: Thu, 10 Oct 2024 20:45:43 +0200 Subject: [PATCH] expfmt/text: optionally include empty MetricFamilies When parsing text format it is sometimes useful to keep all the MetricFamilies around, even the ones that don't have any timeseries. This is the case for the testutil library in client_golang where we need to distinguish registered metrics from erronous inputs. Signed-off-by: Damien Grisonnet --- expfmt/text_create.go | 4 ---- expfmt/text_parse.go | 15 ++++++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/expfmt/text_create.go b/expfmt/text_create.go index 4b86434b..d156a128 100644 --- a/expfmt/text_create.go +++ b/expfmt/text_create.go @@ -76,10 +76,6 @@ var ( // // This method fulfills the type 'prometheus.encoder'. func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (written int, err error) { - // Fail-fast checks. - if len(in.Metric) == 0 { - return 0, fmt.Errorf("MetricFamily has no metrics: %s", in) - } name := in.GetName() if name == "" { return 0, fmt.Errorf("MetricFamily has no name: %s", in) diff --git a/expfmt/text_parse.go b/expfmt/text_parse.go index f085a923..7e2eb980 100644 --- a/expfmt/text_parse.go +++ b/expfmt/text_parse.go @@ -78,6 +78,9 @@ type TextParser struct { // 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 + // Include all MetricFamilies when parsing an input, even the ones for which + // the metric is registered but it doesn't have any timeseries. + includeEmptyMetricFamilies bool } // TextToMetricFamilies reads 'in' as the simple and flat text-based exchange @@ -109,9 +112,11 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF // Magic happens here... } // Get rid of empty metric families. - for k, mf := range p.metricFamiliesByName { - if len(mf.GetMetric()) == 0 { - delete(p.metricFamiliesByName, k) + if !p.includeEmptyMetricFamilies { + for k, mf := range p.metricFamiliesByName { + if len(mf.GetMetric()) == 0 { + delete(p.metricFamiliesByName, k) + } } } // If p.err is io.EOF now, we have run into a premature end of the input @@ -124,6 +129,10 @@ func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricF return p.metricFamiliesByName, p.err } +func (p *TextParser) IncludeEmptyMetricFamilies(b bool) { + p.includeEmptyMetricFamilies = b +} + func (p *TextParser) reset(in io.Reader) { p.metricFamiliesByName = map[string]*dto.MetricFamily{} if p.buf == nil {