Skip to content

Commit

Permalink
Exporting resources attributes on target_info for Prometheus Exporter (
Browse files Browse the repository at this point in the history
…#3279)

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
  • Loading branch information
nerstak and srikanthccv committed Jun 28, 2023
1 parent 6559e07 commit adbcf82
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased


- Add max_scale option to Exponential Bucket Histogram Aggregation
([#3323](https://github.com/open-telemetry/opentelemetry-python/pull/3323))
- Use BoundedAttributes instead of raw dict to extract attributes from LogRecord
Expand Down Expand Up @@ -35,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add speced out environment variables and arguments for BatchLogRecordProcessor
([#3237](https://github.com/open-telemetry/opentelemetry-python/pull/3237))


## Version 1.17.0/0.38b0 (2023-03-22)

- Implement LowMemory temporality
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
CounterMetricFamily,
GaugeMetricFamily,
HistogramMetricFamily,
InfoMetricFamily,
)
from prometheus_client.core import Metric as PrometheusMetric

Expand All @@ -97,6 +98,9 @@

_logger = getLogger(__name__)

_TARGET_INFO_NAME = "target"
_TARGET_INFO_DESCRIPTION = "Target metadata"


def _convert_buckets(
bucket_counts: Sequence[int], explicit_bounds: Sequence[float]
Expand All @@ -116,8 +120,7 @@ def _convert_buckets(
class PrometheusMetricReader(MetricReader):
"""Prometheus metric exporter for OpenTelemetry."""

def __init__(self) -> None:

def __init__(self, disable_target_info: bool = False) -> None:
super().__init__(
preferred_temporality={
Counter: AggregationTemporality.CUMULATIVE,
Expand All @@ -128,7 +131,7 @@ def __init__(self) -> None:
ObservableGauge: AggregationTemporality.CUMULATIVE,
}
)
self._collector = _CustomCollector()
self._collector = _CustomCollector(disable_target_info)
REGISTRY.register(self._collector)
self._collector._callback = self.collect

Expand All @@ -153,12 +156,14 @@ class _CustomCollector:
https://github.com/prometheus/client_python#custom-collectors
"""

def __init__(self):
def __init__(self, disable_target_info: bool = False):
self._callback = None
self._metrics_datas = deque()
self._non_letters_digits_underscore_re = compile(
r"[^\w]", UNICODE | IGNORECASE
)
self._disable_target_info = disable_target_info
self._target_info = None

def add_metrics_data(self, metrics_data: MetricsData) -> None:
"""Add metrics to Prometheus data"""
Expand All @@ -175,6 +180,20 @@ def collect(self) -> None:

metric_family_id_metric_family = {}

if len(self._metrics_datas):
if not self._disable_target_info:
if self._target_info is None:
attributes = {}
for res in self._metrics_datas[0].resource_metrics:
attributes = {**attributes, **res.resource.attributes}

self._target_info = self._create_info_metric(
_TARGET_INFO_NAME, _TARGET_INFO_DESCRIPTION, attributes
)
metric_family_id_metric_family[
_TARGET_INFO_NAME
] = self._target_info

while self._metrics_datas:
self._translate_to_prometheus(
self._metrics_datas.popleft(), metric_family_id_metric_family
Expand Down Expand Up @@ -327,3 +346,11 @@ def _check_value(self, value: Union[int, float, str, Sequence]) -> str:
if not isinstance(value, str):
return dumps(value, default=str)
return str(value)

def _create_info_metric(
self, name: str, description: str, attributes: Dict[str, str]
) -> InfoMetricFamily:
"""Create an Info Metric Family with list of attributes"""
info = InfoMetricFamily(name, description, labels=attributes)
info.add_metric(labels=list(attributes.keys()), value=attributes)
return info
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
from unittest.mock import Mock, patch

from prometheus_client import generate_latest
from prometheus_client.core import CounterMetricFamily, GaugeMetricFamily
from prometheus_client.core import (
CounterMetricFamily,
GaugeMetricFamily,
InfoMetricFamily,
)

from opentelemetry.exporter.prometheus import (
PrometheusMetricReader,
Expand All @@ -33,6 +37,7 @@
ResourceMetrics,
ScopeMetrics,
)
from opentelemetry.sdk.resources import Resource
from opentelemetry.test.metrictestutil import (
_generate_gauge,
_generate_sum,
Expand Down Expand Up @@ -101,7 +106,7 @@ def test_histogram_to_prometheus(self):
]
)

collector = _CustomCollector()
collector = _CustomCollector(disable_target_info=True)
collector.add_metrics_data(metrics_data)
result_bytes = generate_latest(collector)
result = result_bytes.decode("utf-8")
Expand Down Expand Up @@ -146,7 +151,7 @@ def test_sum_to_prometheus(self):
]
)

collector = _CustomCollector()
collector = _CustomCollector(disable_target_info=True)
collector.add_metrics_data(metrics_data)

for prometheus_metric in collector.collect():
Expand Down Expand Up @@ -189,7 +194,7 @@ def test_gauge_to_prometheus(self):
]
)

collector = _CustomCollector()
collector = _CustomCollector(disable_target_info=True)
collector.add_metrics_data(metrics_data)

for prometheus_metric in collector.collect():
Expand Down Expand Up @@ -251,7 +256,7 @@ def test_list_labels(self):
)
]
)
collector = _CustomCollector()
collector = _CustomCollector(disable_target_info=True)
collector.add_metrics_data(metrics_data)

for prometheus_metric in collector.collect():
Expand Down Expand Up @@ -293,3 +298,46 @@ def test_multiple_collection_calls(self):
result_2 = list(metric_reader._collector.collect())
self.assertEqual(result_0, result_1)
self.assertEqual(result_1, result_2)

def test_target_info_enabled_by_default(self):
metric_reader = PrometheusMetricReader()
provider = MeterProvider(
metric_readers=[metric_reader],
resource=Resource({"os": "Unix", "histo": 1}),
)
meter = provider.get_meter("getting-started", "0.1.2")
counter = meter.create_counter("counter")
counter.add(1)
result = list(metric_reader._collector.collect())

for prometheus_metric in result[:0]:
self.assertEqual(type(prometheus_metric), InfoMetricFamily)
self.assertEqual(prometheus_metric.name, "target")
self.assertEqual(
prometheus_metric.documentation, "Target metadata"
)
self.assertTrue(len(prometheus_metric.samples) == 1)
self.assertEqual(prometheus_metric.samples[0].value, 1)
self.assertTrue(len(prometheus_metric.samples[0].labels) == 2)
self.assertEqual(prometheus_metric.samples[0].labels["os"], "Unix")
self.assertEqual(prometheus_metric.samples[0].labels["histo"], "1")

def test_target_info_disabled(self):
metric_reader = PrometheusMetricReader(disable_target_info=True)
provider = MeterProvider(
metric_readers=[metric_reader],
resource=Resource({"os": "Unix", "histo": 1}),
)
meter = provider.get_meter("getting-started", "0.1.2")
counter = meter.create_counter("counter")
counter.add(1)
result = list(metric_reader._collector.collect())

for prometheus_metric in result:
self.assertNotEqual(type(prometheus_metric), InfoMetricFamily)
self.assertNotEqual(prometheus_metric.name, "target")
self.assertNotEqual(
prometheus_metric.documentation, "Target metadata"
)
self.assertNotIn("os", prometheus_metric.samples[0].labels)
self.assertNotIn("histo", prometheus_metric.samples[0].labels)

0 comments on commit adbcf82

Please sign in to comment.