Repository: prometheus/client_python
Pull request #774 (2022-02-16 23:59:27)

Disable exporting of _created metrics via env var
Users can disable the automatic creation of _created metrics by
specifying the environment variable PROMETHEUS_DISABLE_CREATED_SERIES.
This is controlled by an environment variable as an end user may not
have control over which registry is being used/when metrics are created.

Alternatively, we could force this to be done on the registry level and filter the metrics out
but that would not help users with custom registries. Happy to hear feedback that another
approach should be taken though.

Closes: #672  


Patch:
diff --git a/README.md b/README.md
index 9582830..a82dd39 100644
--- a/README.md
+++ b/README.md
@@ -252,6 +252,13 @@ h = Histogram('request_latency_seconds', 'Description of histogram')
 h.observe(4.7, {'trace_id': 'abc123'})
 ```
 
+### Disabling `_created` metrics
+
+By default counters, histograms, and summaries export an additional series
+suffixed with `_created` and a value of the unix timestamp for when the metric
+was created. If this information is not helpful, it can be disabled by setting
+the environment variable `PROMETHEUS_DISABLE_CREATED_SERIES=True`.
+
 ### Process Collector
 
 The Python client automatically exports metrics about process CPU usage, RAM,
diff --git a/prometheus_client/metrics.py b/prometheus_client/metrics.py
index a23056e..7ca31dd 100644
--- a/prometheus_client/metrics.py
+++ b/prometheus_client/metrics.py
@@ -1,3 +1,4 @@
+import os
 from threading import Lock
 import time
 import types
@@ -62,6 +63,13 @@ def _validate_exemplar(exemplar):
         raise ValueError('Exemplar labels have %d UTF-8 characters, exceeding the limit of 128')
 
 
+def _get_use_created() -> bool:
+    return os.environ.get("PROMETHEUS_DISABLE_CREATED_SERIES", 'False').lower() not in ('true', '1', 't')
+
+
+_use_created = _get_use_created()
+
+
 class MetricWrapperBase(Collector):
     _type: Optional[str] = None
     _reserved_labelnames: Sequence[str] = ()
@@ -291,10 +299,13 @@ class Counter(MetricWrapperBase):
         return ExceptionCounter(self, exception)
 
     def _child_samples(self) -> Iterable[Sample]:
-        return (
-            Sample('_total', {}, self._value.get(), None, self._value.get_exemplar()),
-            Sample('_created', {}, self._created, None, None),
-        )
+        sample = Sample('_total', {}, self._value.get(), None, self._value.get_exemplar())
+        if _use_created:
+            return (
+                sample,
+                Sample('_created', {}, self._created, None, None)
+            )
+        return (sample,)
 
 
 class Gauge(MetricWrapperBase):
@@ -484,11 +495,13 @@ class Summary(MetricWrapperBase):
         return Timer(self, 'observe')
 
     def _child_samples(self) -> Iterable[Sample]:
-        return (
+        samples = [
             Sample('_count', {}, self._count.get(), None, None),
             Sample('_sum', {}, self._sum.get(), None, None),
-            Sample('_created', {}, self._created, None, None),
-        )
+        ]
+        if _use_created:
+            samples.append(Sample('_created', {}, self._created, None, None))
+        return tuple(samples)
 
 
 class Histogram(MetricWrapperBase):
@@ -616,7 +629,8 @@ class Histogram(MetricWrapperBase):
         samples.append(Sample('_count', {}, acc, None, None))
         if self._upper_bounds[0] >= 0:
             samples.append(Sample('_sum', {}, self._sum.get(), None, None))
-        samples.append(Sample('_created', {}, self._created, None, None))
+        if _use_created:
+            samples.append(Sample('_created', {}, self._created, None, None))
         return tuple(samples)
 
 


Test patch:
diff --git a/tests/test_core.py b/tests/test_core.py
index 36d521c..6f7c9d1 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1,9 +1,11 @@
 from concurrent.futures import ThreadPoolExecutor
+import os
 import time
 import unittest
 
 import pytest
 
+from prometheus_client import metrics
 from prometheus_client.core import (
     CollectorRegistry, Counter, CounterMetricFamily, Enum, Gauge,
     GaugeHistogramMetricFamily, GaugeMetricFamily, Histogram,
@@ -11,6 +13,7 @@ from prometheus_client.core import (
     StateSetMetricFamily, Summary, SummaryMetricFamily, UntypedMetricFamily,
 )
 from prometheus_client.decorator import getargspec
+from prometheus_client.metrics import _get_use_created
 
 
 def assert_not_observable(fn, *args, **kwargs):
@@ -115,6 +118,32 @@ class TestCounter(unittest.TestCase):
         })
 
 
+class TestDisableCreated(unittest.TestCase):
+    def setUp(self):
+        self.registry = CollectorRegistry()
+        os.environ['PROMETHEUS_DISABLE_CREATED_SERIES'] = 'True'
+        metrics._use_created = _get_use_created()
+
+    def tearDown(self):
+        os.environ.pop('PROMETHEUS_DISABLE_CREATED_SERIES', None)
+        metrics._use_created = _get_use_created()
+
+    def test_counter(self):
+        counter = Counter('c_total', 'help', registry=self.registry)
+        counter.inc()
+        self.assertEqual(None, self.registry.get_sample_value('c_created'))
+
+    def test_histogram(self):
+        histogram = Histogram('h', 'help', registry=self.registry)
+        histogram.observe(3.2)
+        self.assertEqual(None, self.registry.get_sample_value('h_created'))
+
+    def test_summary(self):
+        summary = Summary('s', 'help', registry=self.registry)
+        summary.observe(8.2)
+        self.assertEqual(None, self.registry.get_sample_value('s_created'))
+
+
 class TestGauge(unittest.TestCase):
     def setUp(self):
         self.registry = CollectorRegistry()