func (r *registry) giveMetricFamily(mf *dto.MetricFamily) { mf.Reset() select { case r.metricFamilyPool <- mf: default: } }
func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *dto.Metric, desc *Desc, metricHashes map[uint64]struct{}) error { // Type consistency with metric family. if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil || metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil || metricFamily.GetType() == dto.MetricType_SUMMARY && dtoMetric.Summary == nil || metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil { return fmt.Errorf( "collected metric %q is not a %s", dtoMetric, metricFamily.Type, ) } // Desc consistency with metric family. if metricFamily.GetHelp() != desc.help { return fmt.Errorf( "collected metric %q has help %q but should have %q", dtoMetric, desc.help, metricFamily.GetHelp(), ) } // Is the desc consistent with the content of the metric? lpsFromDesc := make([]*dto.LabelPair, 0, len(dtoMetric.Label)) lpsFromDesc = append(lpsFromDesc, desc.constLabelPairs...) for _, l := range desc.variableLabels { lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{ Name: proto.String(l), }) } if len(lpsFromDesc) != len(dtoMetric.Label) { return fmt.Errorf( "labels in collected metric %q are inconsistent with descriptor %s", dtoMetric, desc, ) } sort.Sort(LabelPairSorter(lpsFromDesc)) for i, lpFromDesc := range lpsFromDesc { lpFromMetric := dtoMetric.Label[i] if lpFromDesc.GetName() != lpFromMetric.GetName() || lpFromDesc.Value != nil && lpFromDesc.GetValue() != lpFromMetric.GetValue() { return fmt.Errorf( "labels in collected metric %q are inconsistent with descriptor %s", dtoMetric, desc, ) } } // Is the metric unique (i.e. no other metric with the same name and the same label values)? h := fnv.New64a() var buf bytes.Buffer buf.WriteString(desc.fqName) buf.WriteByte(model.SeparatorByte) h.Write(buf.Bytes()) for _, lp := range dtoMetric.Label { buf.Reset() buf.WriteString(lp.GetValue()) buf.WriteByte(model.SeparatorByte) h.Write(buf.Bytes()) } metricHash := h.Sum64() if _, exists := metricHashes[metricHash]; exists { return fmt.Errorf( "collected metric %q was collected before with the same name and label values", dtoMetric, ) } metricHashes[metricHash] = struct{}{} r.mtx.RLock() // Remaining checks need the read lock. defer r.mtx.RUnlock() // Is the desc registered? if _, exist := r.descIDs[desc.id]; !exist { return fmt.Errorf("collected metric %q with unregistered descriptor %s", dtoMetric, desc) } return nil }
// MetricFamilyToText converts a MetricFamily proto message into text format and // writes the resulting lines to 'out'. It returns the number of bytes written // and any error encountered. This function does not perform checks on the // content of the metric and label names, i.e. invalid metric or label names // will result in invalid text format output. // This method fulfills the type 'prometheus.encoder'. func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) { var written int // Fail-fast checks. if len(in.Metric) == 0 { return written, fmt.Errorf("MetricFamily has no metrics: %s", in) } name := in.GetName() if name == "" { return written, fmt.Errorf("MetricFamily has no name: %s", in) } if in.Type == nil { return written, fmt.Errorf("MetricFamily has no type: %s", in) } // Comments, first HELP, then TYPE. if in.Help != nil { n, err := fmt.Fprintf( out, "# HELP %s %s\n", name, escapeString(*in.Help, false), ) written += n if err != nil { return written, err } } metricType := in.GetType() n, err := fmt.Fprintf( out, "# TYPE %s %s\n", name, strings.ToLower(metricType.String()), ) written += n if err != nil { return written, err } // Finally the samples, one line for each. for _, metric := range in.Metric { switch metricType { case dto.MetricType_COUNTER: if metric.Counter == nil { return written, fmt.Errorf( "expected counter in metric %s", metric, ) } n, err = writeSample( name, metric, "", "", metric.Counter.GetValue(), out, ) case dto.MetricType_GAUGE: if metric.Gauge == nil { return written, fmt.Errorf( "expected gauge in metric %s", metric, ) } n, err = writeSample( name, metric, "", "", metric.Gauge.GetValue(), out, ) case dto.MetricType_UNTYPED: if metric.Untyped == nil { return written, fmt.Errorf( "expected untyped in metric %s", metric, ) } n, err = writeSample( name, metric, "", "", metric.Untyped.GetValue(), out, ) case dto.MetricType_SUMMARY: if metric.Summary == nil { return written, fmt.Errorf( "expected summary in metric %s", metric, ) } for _, q := range metric.Summary.Quantile { n, err = writeSample( name, metric, "quantile", fmt.Sprint(q.GetQuantile()), q.GetValue(), out, ) written += n if err != nil { return written, err } } n, err = writeSample( name+"_sum", metric, "", "", metric.Summary.GetSampleSum(), out, ) if err != nil { return written, err } written += n n, err = writeSample( name+"_count", metric, "", "", float64(metric.Summary.GetSampleCount()), out, ) case dto.MetricType_HISTOGRAM: if metric.Histogram == nil { return written, fmt.Errorf( "expected summary in metric %s", metric, ) } for _, q := range metric.Histogram.Bucket { n, err = writeSample( name+"_bucket", metric, "le", fmt.Sprint(q.GetUpperBound()), float64(q.GetCumulativeCount()), out, ) written += n if err != nil { return written, err } // TODO: Add +inf bucket if it's missing. } n, err = writeSample( name+"_sum", metric, "", "", metric.Histogram.GetSampleSum(), out, ) if err != nil { return written, err } written += n n, err = writeSample( name+"_count", metric, "", "", float64(metric.Histogram.GetSampleCount()), out, ) default: return written, fmt.Errorf( "unexpected type in metric %s", metric, ) } written += n if err != nil { return written, err } } return written, nil }