metric_registry.pyx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. from library.python.monlib.encoder cimport Encoder
  2. from library.python.monlib.labels cimport TLabels
  3. from library.python.monlib.metric cimport (
  4. Gauge, IntGauge, Counter, Rate, Histogram, IHistogramCollectorPtr,
  5. ExponentialHistogram, ExplicitHistogram, LinearHistogram)
  6. from library.python.monlib.metric_consumer cimport IMetricConsumer
  7. from library.python.monlib.metric_registry cimport TMetricRegistry
  8. from util.generic.ptr cimport THolder
  9. from util.generic.string cimport TString
  10. from util.datetime.base cimport TInstant
  11. from util.system.types cimport ui32
  12. from util.generic.vector cimport TVector
  13. from libcpp.string cimport string
  14. from cython.operator cimport address, dereference as deref
  15. import datetime as dt
  16. import sys
  17. cdef extern from "<utility>" namespace "std" nogil:
  18. cdef IHistogramCollectorPtr&& move(IHistogramCollectorPtr t)
  19. def get_or_raise(kwargs, key):
  20. value = kwargs.get(key)
  21. if value is None:
  22. raise ValueError(key + ' argument is required but not specified')
  23. return value
  24. class HistogramType(object):
  25. Exponential = 0
  26. Explicit = 1
  27. Linear = 2
  28. cdef class MetricRegistry:
  29. """
  30. Represents an entity holding a set of counters of different types identified by labels
  31. Example usage:
  32. .. ::
  33. registry = MetricRegistry()
  34. response_times = registry.histogram_rate(
  35. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  36. HistogramType.Explicit, buckets=[10, 20, 50, 200, 500])
  37. requests = registry.rate({'path': 'ping', 'sensor': 'requestRate'})
  38. uptime = registry.gauge({'sensor': 'serverUptimeSeconds'})
  39. # ...
  40. requests.inc()
  41. uptime.set(time.time() - start_time)
  42. # ...
  43. dumps(registry)
  44. """
  45. cdef THolder[TMetricRegistry] __wrapped
  46. def __cinit__(self, labels=None):
  47. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  48. self.__wrapped.Reset(new TMetricRegistry(native_labels))
  49. @staticmethod
  50. cdef TLabels _py_to_native_labels(dict labels):
  51. cdef TLabels native_labels = TLabels()
  52. if labels is not None:
  53. for name, value in labels.items():
  54. native_labels.Add(TString(<string>name.encode('utf-8')), TString(<string>value.encode('utf-8')))
  55. return native_labels
  56. @staticmethod
  57. cdef _native_to_py_labels(const TLabels& native_labels):
  58. result = dict()
  59. cdef TLabels.const_iterator it = native_labels.begin()
  60. while it != native_labels.end():
  61. name = TString(deref(it).Name())
  62. value = TString(deref(it).Value())
  63. if (isinstance(name, bytes)):
  64. name = name.decode('utf-8')
  65. if (isinstance(value, bytes)):
  66. value = value.decode('utf-8')
  67. result[name] = value
  68. it += 1
  69. return result
  70. def _histogram(self, labels, is_rate, hist_type, **kwargs):
  71. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  72. cdef IHistogramCollectorPtr collector
  73. cdef TVector[double] native_buckets
  74. if hist_type == HistogramType.Exponential:
  75. buckets = int(get_or_raise(kwargs, 'bucket_count'))
  76. base = float(get_or_raise(kwargs, 'base'))
  77. scale = float(kwargs.get('scale', 1.))
  78. collector = move(ExponentialHistogram(buckets, base, scale))
  79. elif hist_type == HistogramType.Explicit:
  80. buckets = get_or_raise(kwargs, 'buckets')
  81. native_buckets = buckets
  82. collector = move(ExplicitHistogram(native_buckets))
  83. elif hist_type == HistogramType.Linear:
  84. buckets = get_or_raise(kwargs, 'bucket_count')
  85. start_value = get_or_raise(kwargs, 'start_value')
  86. bucket_width = get_or_raise(kwargs, 'bucket_width')
  87. collector = move(LinearHistogram(buckets, start_value, bucket_width))
  88. else:
  89. # XXX: string representation
  90. raise ValueError('histogram type {} is not supported'.format(str(hist_type)))
  91. cdef THistogram* native_hist
  92. if is_rate:
  93. native_hist = self.__wrapped.Get().HistogramRate(native_labels, move(collector))
  94. else:
  95. native_hist = self.__wrapped.Get().HistogramCounter(native_labels, move(collector))
  96. return Histogram.from_ptr(native_hist)
  97. @property
  98. def common_labels(self):
  99. """
  100. Gets labels that are common among all the counters in this registry
  101. :returns: Common labels as a dict
  102. """
  103. cdef const TLabels* native = address(self.__wrapped.Get().CommonLabels())
  104. labels = MetricRegistry._native_to_py_labels(deref(native))
  105. return labels
  106. def gauge(self, labels):
  107. """
  108. Gets a gauge counter or creates a new one in case counter with the specified labels
  109. does not exist
  110. :param labels: A dict of labels which identifies counter
  111. :returns: Gauge counter
  112. """
  113. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  114. native_gauge = self.__wrapped.Get().Gauge(native_labels)
  115. return Gauge.from_ptr(native_gauge)
  116. def int_gauge(self, labels):
  117. """
  118. Gets a gauge counter or creates a new one in case counter with the specified labels
  119. does not exist
  120. :param labels: A dict of labels which identifies counter
  121. :returns: IntGauge counter
  122. """
  123. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  124. native_gauge = self.__wrapped.Get().IntGauge(native_labels)
  125. return IntGauge.from_ptr(native_gauge)
  126. def counter(self, labels):
  127. """
  128. Gets a counter or creates a new one in case counter with the specified labels
  129. does not exist
  130. :param labels: A dict of labels which identifies counter
  131. :returns: Counter counter
  132. """
  133. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  134. native_counter = self.__wrapped.Get().Counter(native_labels)
  135. return Counter.from_ptr(native_counter)
  136. def rate(self, labels):
  137. """
  138. Gets a rate counter or creates a new one in case counter with the specified labels
  139. does not exist
  140. :param labels: A dict of labels which identifies counter
  141. :returns: Rate counter
  142. """
  143. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  144. native_rate = self.__wrapped.Get().Rate(native_labels)
  145. return Rate.from_ptr(native_rate)
  146. def histogram_counter(self, labels, hist_type, **kwargs):
  147. """
  148. Gets a histogram counter or creates a new one in case counter with the specified labels
  149. does not exist
  150. :param labels: A dict of labels which identifies counter
  151. :param hist_type: Specifies the way histogram buckets are defined (allowed values: explicit, exponential, linear)
  152. Keyword arguments:
  153. :param buckets: A list of bucket upper bounds (explicit)
  154. :param bucket_count: Number of buckets (linear, exponential)
  155. :param base: the exponential growth factor for buckets' width (exponential)
  156. :param scale: linear scale for the buckets. Must be >= 1.0 (exponential)
  157. :param start_value: the upper bound of the first bucket (linear)
  158. :returns: Histogram counter
  159. Example usage:
  160. .. ::
  161. my_histogram = registry.histogram_counter(
  162. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  163. HistogramType.Explicit, buckets=[10, 20, 50, 200, 500])
  164. # (-inf; 10] (10; 20] (20; 50] (200; 500] (500; +inf)
  165. # or:
  166. my_histogram = registry.histogram_counter(
  167. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  168. HistogramType.Linear, bucket_count=4, bucket_width=10, start_value=0)
  169. # (-inf; 0] (0; 10] (10; 20] (20; +inf)
  170. # or:
  171. my_histogram = registry.histogram_counter(
  172. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  173. HistogramType.Exponential, bucket_count=6, base=2, scale=3)
  174. # (-inf; 3] (3; 6] (6; 12] (12; 24] (24; 48] (48; +inf)
  175. ::
  176. """
  177. return self._histogram(labels, False, hist_type, **kwargs)
  178. def histogram_rate(self, labels, hist_type, **kwargs):
  179. """
  180. Gets a histogram rate counter or creates a new one in case counter with the specified labels
  181. does not exist
  182. :param labels: A dict of labels which identifies counter
  183. :param hist_type: Specifies the way histogram buckets are defined (allowed values: explicit, exponential, linear)
  184. Keyword arguments:
  185. :param buckets: A list of bucket upper bounds (explicit)
  186. :param bucket_count: Number of buckets (linear, exponential)
  187. :param base: the exponential growth factor for buckets' width (exponential)
  188. :param scale: linear scale for the buckets. Must be >= 1.0 (exponential)
  189. :param start_value: the upper bound of the first bucket (linear)
  190. :returns: Histogram counter
  191. Example usage:
  192. .. ::
  193. my_histogram = registry.histogram_counter(
  194. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  195. HistogramType.Explicit, buckets=[10, 20, 50, 200, 500])
  196. # (-inf; 10] (10; 20] (20; 50] (200; 500] (500; +inf)
  197. # or:
  198. my_histogram = registry.histogram_counter(
  199. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  200. HistogramType.Linear, bucket_count=4, bucket_width=10, start_value=0)
  201. # (-inf; 0] (0; 10] (10; 20] (20; +inf)
  202. # or:
  203. my_histogram = registry.histogram_counter(
  204. {'path': 'ping', 'sensor': 'responseTimeMillis'},
  205. HistogramType.Exponential, bucket_count=6, base=2, scale=3)
  206. # (-inf; 3] (3; 6] (6; 12] (12; 24] (24; 48] (48; +inf)
  207. ::
  208. """
  209. return self._histogram(labels, True, hist_type, **kwargs)
  210. def reset(self):
  211. self.__wrapped.Get().Reset()
  212. def accept(self, time, Encoder encoder):
  213. cdef IMetricConsumer* ptr = <IMetricConsumer*>encoder.native()
  214. timestamp = int((time - dt.datetime(1970, 1, 1)).total_seconds())
  215. self.__wrapped.Get().Accept(TInstant.Seconds(timestamp), ptr)
  216. def remove_metric(self, labels):
  217. cdef TLabels native_labels = MetricRegistry._py_to_native_labels(labels)
  218. self.__wrapped.Get().RemoveMetric(native_labels)