Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)