fix(ext/prometheus): support minmaxsumcount aggregator (#945)

This commit is contained in:
Christoph Brand 2020-09-24 15:47:29 +02:00 committed by alrex
parent b090a6bd49
commit 6e3965be16
2 changed files with 58 additions and 24 deletions

View File

@ -67,22 +67,22 @@ API
import collections
import logging
import re
from typing import Sequence
from typing import Iterable, Optional, Sequence, Union
from prometheus_client import start_http_server
from prometheus_client.core import (
REGISTRY,
CollectorRegistry,
CounterMetricFamily,
SummaryMetricFamily,
UnknownMetricFamily,
)
from opentelemetry.metrics import Counter, Metric, ValueRecorder
from opentelemetry.metrics import Counter, ValueRecorder
from opentelemetry.sdk.metrics.export import (
MetricRecord,
MetricsExporter,
MetricsExportResult,
)
from opentelemetry.sdk.metrics.export.aggregate import MinMaxSumCountAggregator
logger = logging.getLogger(__name__)
@ -121,7 +121,7 @@ class CustomCollector:
r"[^\w]", re.UNICODE | re.IGNORECASE
)
def add_metrics_data(self, metric_records: Sequence[MetricRecord]):
def add_metrics_data(self, metric_records: Sequence[MetricRecord]) -> None:
self._metrics_to_export.append(metric_records)
def collect(self):
@ -152,25 +152,35 @@ class CustomCollector:
metric_name = self._prefix + "_"
metric_name += self._sanitize(metric_record.instrument.name)
description = getattr(metric_record.instrument, "description", "")
if isinstance(metric_record.instrument, Counter):
prometheus_metric = CounterMetricFamily(
name=metric_name,
documentation=metric_record.instrument.description,
labels=label_keys,
name=metric_name, documentation=description, labels=label_keys
)
prometheus_metric.add_metric(
labels=label_values, value=metric_record.aggregator.checkpoint
)
# TODO: Add support for histograms when supported in OT
elif isinstance(metric_record.instrument, ValueRecorder):
prometheus_metric = UnknownMetricFamily(
value = metric_record.aggregator.checkpoint
if isinstance(metric_record.aggregator, MinMaxSumCountAggregator):
prometheus_metric = SummaryMetricFamily(
name=metric_name,
documentation=metric_record.instrument.description,
documentation=description,
labels=label_keys,
)
prometheus_metric.add_metric(
labels=label_values, value=metric_record.aggregator.checkpoint
labels=label_values,
count_value=value.count,
sum_value=value.sum,
)
else:
prometheus_metric = UnknownMetricFamily(
name=metric_name,
documentation=description,
labels=label_keys,
)
prometheus_metric.add_metric(labels=label_values, value=value)
else:
logger.warning(
@ -178,7 +188,7 @@ class CustomCollector:
)
return prometheus_metric
def _sanitize(self, key):
def _sanitize(self, key: str) -> str:
"""sanitize the given metric name or label according to Prometheus rule.
Replace all characters other than [A-Za-z0-9_] with '_'.
"""

View File

@ -15,6 +15,7 @@
import unittest
from unittest import mock
from prometheus_client import generate_latest
from prometheus_client.core import CounterMetricFamily
from opentelemetry.exporter.prometheus import (
@ -24,7 +25,11 @@ from opentelemetry.exporter.prometheus import (
from opentelemetry.metrics import get_meter_provider, set_meter_provider
from opentelemetry.sdk import metrics
from opentelemetry.sdk.metrics.export import MetricRecord, MetricsExportResult
from opentelemetry.sdk.metrics.export.aggregate import SumAggregator
from opentelemetry.sdk.metrics.export.aggregate import (
MinMaxSumCountAggregator,
SumAggregator,
)
from opentelemetry.sdk.util import get_dict_as_key
class TestPrometheusMetricExporter(unittest.TestCase):
@ -35,7 +40,7 @@ class TestPrometheusMetricExporter(unittest.TestCase):
"testname", "testdesc", "unit", int, metrics.Counter,
)
labels = {"environment": "staging"}
self._labels_key = metrics.get_dict_as_key(labels)
self._labels_key = get_dict_as_key(labels)
self._mock_registry_register = mock.Mock()
self._registry_register_patch = mock.patch(
@ -70,13 +75,32 @@ class TestPrometheusMetricExporter(unittest.TestCase):
self.assertEqual(len(exporter._collector._metrics_to_export), 1)
self.assertIs(result, MetricsExportResult.SUCCESS)
def test_min_max_sum_aggregator_to_prometheus(self):
meter = get_meter_provider().get_meter(__name__)
metric = meter.create_metric(
"test@name", "testdesc", "unit", int, metrics.ValueRecorder, []
)
labels = {}
key_labels = get_dict_as_key(labels)
aggregator = MinMaxSumCountAggregator()
aggregator.update(123)
aggregator.update(456)
aggregator.take_checkpoint()
record = MetricRecord(metric, key_labels, aggregator)
collector = CustomCollector("testprefix")
collector.add_metrics_data([record])
result_bytes = generate_latest(collector)
result = result_bytes.decode("utf-8")
self.assertIn("testprefix_test_name_count 2.0", result)
self.assertIn("testprefix_test_name_sum 579.0", result)
def test_counter_to_prometheus(self):
meter = get_meter_provider().get_meter(__name__)
metric = meter.create_metric(
"test@name", "testdesc", "unit", int, metrics.Counter,
)
labels = {"environment@": "staging", "os": "Windows"}
key_labels = metrics.get_dict_as_key(labels)
key_labels = get_dict_as_key(labels)
aggregator = SumAggregator()
aggregator.update(123)
aggregator.take_checkpoint()
@ -107,7 +131,7 @@ class TestPrometheusMetricExporter(unittest.TestCase):
"tesname", "testdesc", "unit", int, StubMetric
)
labels = {"environment": "staging"}
key_labels = metrics.get_dict_as_key(labels)
key_labels = get_dict_as_key(labels)
record = MetricRecord(metric, key_labels, None)
collector = CustomCollector("testprefix")
collector.add_metrics_data([record])