diff options
author | robot-ydb-importer <robot-ydb-importer@yandex-team.com> | 2024-02-14 19:47:36 +0300 |
---|---|---|
committer | Innokentii Mokin <innokentii@ydb.tech> | 2024-02-16 18:35:13 +0000 |
commit | d6ee6054676c603f8afb27b5bd8ce7fe0a5bfbc0 (patch) | |
tree | 4aa69116e7818a4aae0bfedbfa29639b0f0b90e8 /library/python/monlib/metric_registry.pyx | |
parent | 59ded8ecfcd805c109471346a0d4d1f269bdaa59 (diff) | |
download | ydb-d6ee6054676c603f8afb27b5bd8ce7fe0a5bfbc0.tar.gz |
YDB Import 566
96265cd0cc64e1b9bb31fe97b915ed2a09caf1cb
Diffstat (limited to 'library/python/monlib/metric_registry.pyx')
-rw-r--r-- | library/python/monlib/metric_registry.pyx | 277 |
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) |