.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
models.py
Go to the documentation of this file.
1# SPDX-License-Identifier: AGPL-3.0-or-later
2# pylint: disable=missing-module-docstring
3
4import decimal
5import threading
6
7from searx import logger
8
9
10__all__ = ["Histogram", "HistogramStorage", "CounterStorage"]
11
12logger = logger.getChild('searx.metrics')
13
14
15class Histogram: # pylint: disable=missing-class-docstring
16
17 _slots__ = '_lock', '_size', '_sum', '_quartiles', '_count', '_width'
18
19 def __init__(self, width=10, size=200):
20 self._lock = threading.Lock()
21 self._width = width
22 self._size = size
23 self._quartiles = [0] * size
24 self._count = 0
25 self._sum = 0
26
27 def observe(self, value):
28 q = int(value / self._width)
29 if q < 0: # pylint: disable=consider-using-max-builtin
30 # Value below zero is ignored
31 q = 0
32 if q >= self._size:
33 # Value above the maximum is replaced by the maximum
34 q = self._size - 1
35 with self._lock:
36 self._quartiles[q] += 1
37 self._count += 1
38 self._sum += value
39
40 @property
41 def quartiles(self):
42 return list(self._quartiles)
43
44 @property
45 def count(self):
46 return self._count
47
48 @property
49 def sum(self):
50 return self._sum
51
52 @property
53 def average(self):
54 with self._lock:
55 if self._count != 0:
56 return self._sum / self._count
57 return 0
58
59 @property
61 '''Quartile in percentage'''
62 with self._lock:
63 if self._count > 0:
64 return [int(q * 100 / self._count) for q in self._quartiles]
65 return self._quartiles
66
67 @property
69 result = {}
70 # use Decimal to avoid rounding errors
71 x = decimal.Decimal(0)
72 width = decimal.Decimal(self._width)
73 width_exponent = -width.as_tuple().exponent
74 with self._lock:
75 if self._count > 0:
76 for y in self._quartiles:
77 yp = int(y * 100 / self._count) # pylint: disable=invalid-name
78 if yp != 0:
79 result[round(float(x), width_exponent)] = yp
80 x += width
81 return result
82
83 def percentage(self, percentage):
84 # use Decimal to avoid rounding errors
85 x = decimal.Decimal(0)
86 width = decimal.Decimal(self._width)
87 stop_at_value = decimal.Decimal(self._count) / 100 * percentage
88 sum_value = 0
89 with self._lock:
90 if self._count > 0:
91 for y in self._quartiles:
92 sum_value += y
93 if sum_value >= stop_at_value:
94 return x
95 x += width
96 return None
97
98 def __repr__(self):
99 return "Histogram<avg: " + str(self.averageaverage) + ", count: " + str(self._count) + ">"
100
101
102class HistogramStorage: # pylint: disable=missing-class-docstring
103
104 __slots__ = 'measures', 'histogram_class'
105
106 def __init__(self, histogram_class=Histogram):
107 self.clear()
108 self.histogram_class = histogram_class
109
110 def clear(self):
111 self.measures = {}
112
113 def configure(self, width, size, *args):
114 measure = self.histogram_class(width, size)
115 self.measures[args] = measure
116 return measure
117
118 def get(self, *args):
119 return self.measures.get(args, None)
120
121 def dump(self):
122 logger.debug("Histograms:")
123 ks = sorted(self.measures.keys(), key='/'.join) # pylint: disable=invalid-name
124 for k in ks:
125 logger.debug("- %-60s %s", '|'.join(k), self.measures[k])
126
127
128class CounterStorage: # pylint: disable=missing-class-docstring
129
130 __slots__ = 'counters', 'lock'
131
132 def __init__(self):
133 self.lock = threading.Lock()
134 self.clear()
135
136 def clear(self):
137 with self.lock:
138 self.counters = {}
139
140 def configure(self, *args):
141 with self.lock:
142 self.counters[args] = 0
143
144 def get(self, *args):
145 return self.counters[args]
146
147 def add(self, value, *args):
148 with self.lock:
149 self.counters[args] += value
150
151 def dump(self):
152 with self.lock:
153 ks = sorted(self.counters.keys(), key='/'.join) # pylint: disable=invalid-name
154 logger.debug("Counters:")
155 for k in ks:
156 logger.debug("- %-60s %s", '|'.join(k), self.counters[k])
157
158
159class VoidHistogram(Histogram): # pylint: disable=missing-class-docstring
160 def observe(self, value):
161 pass
162
163
164class VoidCounterStorage(CounterStorage): # pylint: disable=missing-class-docstring
165 def add(self, value, *args):
166 pass
__init__(self, histogram_class=Histogram)
Definition models.py:106
configure(self, width, size, *args)
Definition models.py:113
percentage(self, percentage)
Definition models.py:83
__init__(self, width=10, size=200)
Definition models.py:19