aboutsummaryrefslogtreecommitdiffstats
path: root/library/python/monlib/metric_registry.pyx
diff options
context:
space:
mode:
authorrobot-ydb-importer <robot-ydb-importer@yandex-team.com>2024-02-14 19:47:36 +0300
committerrobot-ydb-importer <robot-ydb-importer@yandex-team.com>2024-02-14 21:51:48 +0300
commitccc9ad1a6914b4cce50935b1b3fd868ed69fed13 (patch)
tree9dea935eaf96e944bf8262a295eb8bccb7bce077 /library/python/monlib/metric_registry.pyx
parent37ca0ae098448d6f7d13b7c651f38c282915ad3a (diff)
downloadydb-ccc9ad1a6914b4cce50935b1b3fd868ed69fed13.tar.gz
YDB Import 566
96265cd0cc64e1b9bb31fe97b915ed2a09caf1cb
Diffstat (limited to 'library/python/monlib/metric_registry.pyx')
-rw-r--r--library/python/monlib/metric_registry.pyx277
1 files changed, 277 insertions, 0 deletions
diff --git a/library/python/monlib/metric_registry.pyx b/library/python/monlib/metric_registry.pyx
new file mode 100644
index 0000000000..800a1abd1b
--- /dev/null
+++ b/library/python/monlib/metric_registry.pyx
@@ -0,0 +1,277 @@
+from library.python.monlib.encoder cimport Encoder
+from library.python.monlib.labels cimport TLabels
+from library.python.monlib.metric cimport (
+ Gauge, IntGauge, Counter, Rate, Histogram, IHistogramCollectorPtr,
+ ExponentialHistogram, ExplicitHistogram, LinearHistogram)
+from library.python.monlib.metric_consumer cimport IMetricConsumer
+from library.python.monlib.metric_registry cimport TMetricRegistry
+
+from util.generic.ptr cimport THolder
+from util.generic.string cimport TString
+from util.datetime.base cimport TInstant
+from util.system.types cimport ui32
+from util.generic.vector cimport TVector
+
+from libcpp.string cimport string
+
+from cython.operator cimport address, dereference as deref
+
+import datetime as dt
+import sys
+
+
+cdef extern from "<utility>" namespace "std" nogil:
+ cdef IHistogramCollectorPtr&& move(IHistogramCollectorPtr t)
+
+
+def get_or_raise(kwargs, key):
+ value = kwargs.get(key)
+ if value is None:
+ raise ValueError(key + ' argument is required but not specified')
+
+ return value
+
+
+class HistogramType(object):
+ Exponential = 0
+ Explicit = 1
+ Linear = 2
+
+
+cdef class MetricRegistry:
+ """
+ Represents an entity holding a set of counters of different types identified by labels
+
+ Example usage:
+ .. ::
+ registry = MetricRegistry()
+
+ response_times = registry.histogram_rate(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Explicit, buckets=[10, 20, 50, 200, 500])
+
+ requests = registry.rate({'path': 'ping', 'sensor': 'requestRate'})
+ uptime = registry.gauge({'sensor': 'serverUptimeSeconds'})
+
+ # ...
+ requests.inc()
+ uptime.set(time.time() - start_time)
+
+ # ...
+ dumps(registry)
+ """
+ cdef THolder[TMetricRegistry] __wrapped
+
+ def __cinit__(self, labels=None):
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ self.__wrapped.Reset(new TMetricRegistry(native_labels))
+
+ @staticmethod
+ cdef TLabels _py_to_native_labels(dict labels):
+ cdef TLabels native_labels = TLabels()
+
+ if labels is not None:
+ for name, value in labels.items():
+ native_labels.Add(TString(<string>name.encode('utf-8')), TString(<string>value.encode('utf-8')))
+
+ return native_labels
+
+ @staticmethod
+ cdef _native_to_py_labels(const TLabels& native_labels):
+ result = dict()
+
+ cdef TLabels.const_iterator it = native_labels.begin()
+ while it != native_labels.end():
+ name = TString(deref(it).Name())
+ value = TString(deref(it).Value())
+ if (isinstance(name, bytes)):
+ name = name.decode('utf-8')
+
+ if (isinstance(value, bytes)):
+ value = value.decode('utf-8')
+
+ result[name] = value
+ it += 1
+
+ return result
+
+ def _histogram(self, labels, is_rate, hist_type, **kwargs):
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ cdef IHistogramCollectorPtr collector
+ cdef TVector[double] native_buckets
+
+ if hist_type == HistogramType.Exponential:
+ buckets = int(get_or_raise(kwargs, 'bucket_count'))
+ base = float(get_or_raise(kwargs, 'base'))
+ scale = float(kwargs.get('scale', 1.))
+ collector = move(ExponentialHistogram(buckets, base, scale))
+ elif hist_type == HistogramType.Explicit:
+ buckets = get_or_raise(kwargs, 'buckets')
+ native_buckets = buckets
+ collector = move(ExplicitHistogram(native_buckets))
+ elif hist_type == HistogramType.Linear:
+ buckets = get_or_raise(kwargs, 'bucket_count')
+ start_value = get_or_raise(kwargs, 'start_value')
+ bucket_width = get_or_raise(kwargs, 'bucket_width')
+ collector = move(LinearHistogram(buckets, start_value, bucket_width))
+ else:
+ # XXX: string representation
+ raise ValueError('histogram type {} is not supported'.format(str(hist_type)))
+
+ cdef THistogram* native_hist
+ if is_rate:
+ native_hist = self.__wrapped.Get().HistogramRate(native_labels, move(collector))
+ else:
+ native_hist = self.__wrapped.Get().HistogramCounter(native_labels, move(collector))
+
+ return Histogram.from_ptr(native_hist)
+
+ @property
+ def common_labels(self):
+ """
+ Gets labels that are common among all the counters in this registry
+
+ :returns: Common labels as a dict
+ """
+ cdef const TLabels* native = address(self.__wrapped.Get().CommonLabels())
+ labels = MetricRegistry._native_to_py_labels(deref(native))
+
+ return labels
+
+ def gauge(self, labels):
+ """
+ Gets a gauge counter or creates a new one in case counter with the specified labels
+ does not exist
+
+ :param labels: A dict of labels which identifies counter
+ :returns: Gauge counter
+ """
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ native_gauge = self.__wrapped.Get().Gauge(native_labels)
+ return Gauge.from_ptr(native_gauge)
+
+ def int_gauge(self, labels):
+ """
+ Gets a gauge counter or creates a new one in case counter with the specified labels
+ does not exist
+
+ :param labels: A dict of labels which identifies counter
+ :returns: IntGauge counter
+ """
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ native_gauge = self.__wrapped.Get().IntGauge(native_labels)
+ return IntGauge.from_ptr(native_gauge)
+
+ def counter(self, labels):
+ """
+ Gets a counter or creates a new one in case counter with the specified labels
+ does not exist
+
+ :param labels: A dict of labels which identifies counter
+ :returns: Counter counter
+ """
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ native_counter = self.__wrapped.Get().Counter(native_labels)
+ return Counter.from_ptr(native_counter)
+
+ def rate(self, labels):
+ """
+ Gets a rate counter or creates a new one in case counter with the specified labels
+ does not exist
+
+ :param labels: A dict of labels which identifies counter
+ :returns: Rate counter
+ """
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ native_rate = self.__wrapped.Get().Rate(native_labels)
+ return Rate.from_ptr(native_rate)
+
+ def histogram_counter(self, labels, hist_type, **kwargs):
+ """
+ Gets a histogram counter or creates a new one in case counter with the specified labels
+ does not exist
+
+ :param labels: A dict of labels which identifies counter
+ :param hist_type: Specifies the way histogram buckets are defined (allowed values: explicit, exponential, linear)
+
+ Keyword arguments:
+ :param buckets: A list of bucket upper bounds (explicit)
+ :param bucket_count: Number of buckets (linear, exponential)
+ :param base: the exponential growth factor for buckets' width (exponential)
+ :param scale: linear scale for the buckets. Must be >= 1.0 (exponential)
+ :param start_value: the upper bound of the first bucket (linear)
+
+ :returns: Histogram counter
+
+ Example usage:
+ .. ::
+ my_histogram = registry.histogram_counter(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Explicit, buckets=[10, 20, 50, 200, 500])
+ # (-inf; 10] (10; 20] (20; 50] (200; 500] (500; +inf)
+
+ # or:
+ my_histogram = registry.histogram_counter(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Linear, bucket_count=4, bucket_width=10, start_value=0)
+ # (-inf; 0] (0; 10] (10; 20] (20; +inf)
+
+ # or:
+ my_histogram = registry.histogram_counter(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Exponential, bucket_count=6, base=2, scale=3)
+ # (-inf; 3] (3; 6] (6; 12] (12; 24] (24; 48] (48; +inf)
+ ::
+ """
+ return self._histogram(labels, False, hist_type, **kwargs)
+
+ def histogram_rate(self, labels, hist_type, **kwargs):
+ """
+ Gets a histogram rate counter or creates a new one in case counter with the specified labels
+ does not exist
+
+ :param labels: A dict of labels which identifies counter
+ :param hist_type: Specifies the way histogram buckets are defined (allowed values: explicit, exponential, linear)
+
+ Keyword arguments:
+ :param buckets: A list of bucket upper bounds (explicit)
+ :param bucket_count: Number of buckets (linear, exponential)
+ :param base: the exponential growth factor for buckets' width (exponential)
+ :param scale: linear scale for the buckets. Must be >= 1.0 (exponential)
+ :param start_value: the upper bound of the first bucket (linear)
+
+ :returns: Histogram counter
+
+ Example usage:
+ .. ::
+ my_histogram = registry.histogram_counter(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Explicit, buckets=[10, 20, 50, 200, 500])
+ # (-inf; 10] (10; 20] (20; 50] (200; 500] (500; +inf)
+
+ # or:
+ my_histogram = registry.histogram_counter(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Linear, bucket_count=4, bucket_width=10, start_value=0)
+ # (-inf; 0] (0; 10] (10; 20] (20; +inf)
+
+ # or:
+ my_histogram = registry.histogram_counter(
+ {'path': 'ping', 'sensor': 'responseTimeMillis'},
+ HistogramType.Exponential, bucket_count=6, base=2, scale=3)
+ # (-inf; 3] (3; 6] (6; 12] (12; 24] (24; 48] (48; +inf)
+ ::
+ """
+ return self._histogram(labels, True, hist_type, **kwargs)
+
+ def reset(self):
+ self.__wrapped.Get().Reset()
+
+ def accept(self, time, Encoder encoder):
+ cdef IMetricConsumer* ptr = <IMetricConsumer*>encoder.native()
+ timestamp = int((time - dt.datetime(1970, 1, 1)).total_seconds())
+ self.__wrapped.Get().Accept(TInstant.Seconds(timestamp), ptr)
+
+ def remove_metric(self, labels):
+ cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
+ self.__wrapped.Get().RemoveMetric(native_labels)