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