def collect()

in elasticapm/metrics/base_metrics.py [0:0]


    def collect(self):
        """
        Collects all metrics attached to this metricset, and returns it as a generator
        with one or more elements. More than one element is returned if labels are used.

        The format of the return value should be

            {
                "samples": {"metric.name": {"value": some_float}, ...},
                "timestamp": unix epoch in microsecond precision
            }
        """
        self.before_collect()
        timestamp = int(time.time() * 1000000)
        samples = defaultdict(dict)
        if self._counters:
            # iterate over a copy of the dict to avoid threading issues, see #717
            for (name, labels), counter in self._counters.copy().items():
                if counter is not noop_metric:
                    val = counter.val
                    if val or not counter.reset_on_collect:
                        samples[labels].update({name: {"value": val}})
                    if counter.reset_on_collect:
                        counter.reset()
        if self._gauges:
            for (name, labels), gauge in self._gauges.copy().items():
                if gauge is not noop_metric:
                    val = gauge.val
                    if val or not gauge.reset_on_collect:
                        samples[labels].update({name: {"value": val, "type": "gauge"}})
                    if gauge.reset_on_collect:
                        gauge.reset()
        if self._timers:
            for (name, labels), timer in self._timers.copy().items():
                if timer is not noop_metric:
                    val, count = timer.val
                    if val or not timer.reset_on_collect:
                        sum_name = ".sum"
                        if timer._unit:
                            sum_name += "." + timer._unit
                        samples[labels].update({name + sum_name: {"value": val}})
                        samples[labels].update({name + ".count": {"value": count}})
                    if timer.reset_on_collect:
                        timer.reset()
        if self._histograms:
            for (name, labels), histo in self._histograms.copy().items():
                if histo is not noop_metric:
                    counts = histo.val
                    if counts or not histo.reset_on_collect:
                        # 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."
                        bucket_midpoints = []
                        for i, bucket_le in enumerate(histo.buckets):
                            if i == 0:
                                if bucket_le > 0:
                                    bucket_le /= 2.0
                            elif i == len(histo.buckets) - 1:
                                bucket_le = histo.buckets[i - 1]
                            else:
                                bucket_le = histo.buckets[i - 1] + (bucket_le - histo.buckets[i - 1]) / 2.0
                            bucket_midpoints.append(bucket_le)
                        samples[labels].update(
                            {name: {"counts": counts, "values": bucket_midpoints, "type": "histogram"}}
                        )
                    if histo.reset_on_collect:
                        histo.reset()

        if samples:
            for labels, sample in samples.items():
                result = {"samples": sample, "timestamp": timestamp}
                if labels:
                    result["tags"] = {k: v for k, v in labels}
                yield self.before_yield(result)