in module/apmprometheus/gatherer.go [42:165]
func (g gatherer) GatherMetrics(ctx context.Context, out *apm.Metrics) error {
metricFamilies, err := g.p.Gather()
if err != nil {
return errors.WithStack(err)
}
for _, mf := range metricFamilies {
name := mf.GetName()
switch mf.GetType() {
case dto.MetricType_COUNTER:
for _, m := range mf.GetMetric() {
v := m.GetCounter().GetValue()
out.Add(name, makeLabels(m.GetLabel()), v)
}
case dto.MetricType_GAUGE:
metrics := mf.GetMetric()
if name == "go_info" && len(metrics) == 1 && metrics[0].GetGauge().GetValue() == 1 {
// Ignore the "go_info" metric from the
// built-in GoCollector, as we provide
// the same information in the payload.
continue
}
for _, m := range metrics {
v := m.GetGauge().GetValue()
out.Add(name, makeLabels(m.GetLabel()), v)
}
case dto.MetricType_UNTYPED:
for _, m := range mf.GetMetric() {
v := m.GetUntyped().GetValue()
out.Add(name, makeLabels(m.GetLabel()), v)
}
case dto.MetricType_SUMMARY:
for _, m := range mf.GetMetric() {
s := m.GetSummary()
labels := makeLabels(m.GetLabel())
out.Add(name+".count", labels, float64(s.GetSampleCount()))
out.Add(name+".total", labels, float64(s.GetSampleSum()))
for _, q := range s.GetQuantile() {
p := int(q.GetQuantile() * 100)
out.Add(name+".percentile."+strconv.Itoa(p), labels, q.GetValue())
}
}
case dto.MetricType_HISTOGRAM:
// For the bucket values, we follow the approach described by Prometheus's
// histogram_quantile function (https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile)
// to achieve consistent percentile aggregation results:
//
// "The histogram_quantile() function interpolates quantile values by assuming a linear
// distribution within a bucket. (...) If a quantile is located in the highest bucket,
// the upper bound of the second highest bucket is returned. A lower limit of the lowest
// bucket is assumed to be 0 if the upper bound of that bucket is greater than 0. In that
// case, the usual linear interpolation is applied within that bucket. Otherwise, the upper
// bound of the lowest bucket is returned for quantiles located in the lowest bucket."
for _, m := range mf.GetMetric() {
h := m.GetHistogram()
// Total count for all values in this
// histogram. We want the per value count.
totalCount := h.GetSampleCount()
if totalCount == 0 {
continue
}
labels := makeLabels(m.GetLabel())
values := h.GetBucket()
// The +Inf bucket isn't encoded into the
// protobuf representation, but observations
// that fall within it are reflected in the
// histogram's SampleCount.
// We compare the totalCount to the bucketCount
// (sum of all CumulativeCount()s per bucket)
// to infer if an additional midpoint + count
// need to be added to their respective slices.
var bucketCount uint64
valuesLen := len(values)
midpoints := make([]float64, 0, valuesLen)
counts := make([]uint64, 0, valuesLen)
for i, b := range values {
count := b.GetCumulativeCount()
le := b.GetUpperBound()
if i == 0 {
if le > 0 {
le /= 2
}
} else {
// apm-server expects non-cumulative
// counts. prometheus counts each
// bucket cumulatively, ie. bucketN
// contains all counts for bucketN and
// all counts in preceding values. To
// get the current bucket's count we
// subtract bucketN-1 from bucketN,
// when N>0.
count = count - values[i-1].GetCumulativeCount()
le = values[i-1].GetUpperBound() + (le-values[i-1].GetUpperBound())/2.0
}
// we are excluding zero-count
// prometheus buckets.
// the cumulative count may have
// initially been non-zero, but when we
// subtract the preceding bucket, it
// may end up having a zero count.
if count == 0 {
continue
}
bucketCount += count
counts = append(counts, count)
midpoints = append(midpoints, le)
}
// Check if there were observations that fell
// outside of the defined histogram buckets, so
// we need to modify the current final bucket,
// and add an additional bucket with these
// observations.
if infBucketCount := totalCount - bucketCount; infBucketCount > 0 && valuesLen > 0 {
// Set the midpoint for the +Inf bucket
// to be the final defined bucket value.
midpoints = append(midpoints, values[valuesLen-1].GetUpperBound())
counts = append(counts, infBucketCount)
}
out.AddHistogram(name, labels, midpoints, counts)
}
default:
}
}
return nil
}