mirror of
				https://github.com/searxng/searxng.git
				synced 2025-10-31 18:47:07 -04:00 
			
		
		
		
	In the past, some files were tested with the standard profile, others with a profile in which most of the messages were switched off ... some files were not checked at all. - ``PYLINT_SEARXNG_DISABLE_OPTION`` has been abolished - the distinction ``# lint: pylint`` is no longer necessary - the pylint tasks have been reduced from three to two 1. ./searx/engines -> lint engines with additional builtins 2. ./searx ./searxng_extra ./tests -> lint all other python files Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
		
			
				
	
	
		
			167 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			167 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # SPDX-License-Identifier: AGPL-3.0-or-later
 | |
| # pylint: disable=missing-module-docstring
 | |
| 
 | |
| import decimal
 | |
| import threading
 | |
| 
 | |
| from searx import logger
 | |
| 
 | |
| 
 | |
| __all__ = ["Histogram", "HistogramStorage", "CounterStorage"]
 | |
| 
 | |
| logger = logger.getChild('searx.metrics')
 | |
| 
 | |
| 
 | |
| class Histogram:  # pylint: disable=missing-class-docstring
 | |
| 
 | |
|     _slots__ = '_lock', '_size', '_sum', '_quartiles', '_count', '_width'
 | |
| 
 | |
|     def __init__(self, width=10, size=200):
 | |
|         self._lock = threading.Lock()
 | |
|         self._width = width
 | |
|         self._size = size
 | |
|         self._quartiles = [0] * size
 | |
|         self._count = 0
 | |
|         self._sum = 0
 | |
| 
 | |
|     def observe(self, value):
 | |
|         q = int(value / self._width)
 | |
|         if q < 0:  # pylint: disable=consider-using-max-builtin
 | |
|             # Value below zero is ignored
 | |
|             q = 0
 | |
|         if q >= self._size:
 | |
|             # Value above the maximum is replaced by the maximum
 | |
|             q = self._size - 1
 | |
|         with self._lock:
 | |
|             self._quartiles[q] += 1
 | |
|             self._count += 1
 | |
|             self._sum += value
 | |
| 
 | |
|     @property
 | |
|     def quartiles(self):
 | |
|         return list(self._quartiles)
 | |
| 
 | |
|     @property
 | |
|     def count(self):
 | |
|         return self._count
 | |
| 
 | |
|     @property
 | |
|     def sum(self):
 | |
|         return self._sum
 | |
| 
 | |
|     @property
 | |
|     def average(self):
 | |
|         with self._lock:
 | |
|             if self._count != 0:
 | |
|                 return self._sum / self._count
 | |
|             return 0
 | |
| 
 | |
|     @property
 | |
|     def quartile_percentage(self):
 | |
|         '''Quartile in percentage'''
 | |
|         with self._lock:
 | |
|             if self._count > 0:
 | |
|                 return [int(q * 100 / self._count) for q in self._quartiles]
 | |
|             return self._quartiles
 | |
| 
 | |
|     @property
 | |
|     def quartile_percentage_map(self):
 | |
|         result = {}
 | |
|         # use Decimal to avoid rounding errors
 | |
|         x = decimal.Decimal(0)
 | |
|         width = decimal.Decimal(self._width)
 | |
|         width_exponent = -width.as_tuple().exponent
 | |
|         with self._lock:
 | |
|             if self._count > 0:
 | |
|                 for y in self._quartiles:
 | |
|                     yp = int(y * 100 / self._count)  # pylint: disable=invalid-name
 | |
|                     if yp != 0:
 | |
|                         result[round(float(x), width_exponent)] = yp
 | |
|                     x += width
 | |
|         return result
 | |
| 
 | |
|     def percentage(self, percentage):
 | |
|         # use Decimal to avoid rounding errors
 | |
|         x = decimal.Decimal(0)
 | |
|         width = decimal.Decimal(self._width)
 | |
|         stop_at_value = decimal.Decimal(self._count) / 100 * percentage
 | |
|         sum_value = 0
 | |
|         with self._lock:
 | |
|             if self._count > 0:
 | |
|                 for y in self._quartiles:
 | |
|                     sum_value += y
 | |
|                     if sum_value >= stop_at_value:
 | |
|                         return x
 | |
|                     x += width
 | |
|         return None
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return "Histogram<avg: " + str(self.average) + ", count: " + str(self._count) + ">"
 | |
| 
 | |
| 
 | |
| class HistogramStorage:  # pylint: disable=missing-class-docstring
 | |
| 
 | |
|     __slots__ = 'measures', 'histogram_class'
 | |
| 
 | |
|     def __init__(self, histogram_class=Histogram):
 | |
|         self.clear()
 | |
|         self.histogram_class = histogram_class
 | |
| 
 | |
|     def clear(self):
 | |
|         self.measures = {}
 | |
| 
 | |
|     def configure(self, width, size, *args):
 | |
|         measure = self.histogram_class(width, size)
 | |
|         self.measures[args] = measure
 | |
|         return measure
 | |
| 
 | |
|     def get(self, *args):
 | |
|         return self.measures.get(args, None)
 | |
| 
 | |
|     def dump(self):
 | |
|         logger.debug("Histograms:")
 | |
|         ks = sorted(self.measures.keys(), key='/'.join)  # pylint: disable=invalid-name
 | |
|         for k in ks:
 | |
|             logger.debug("- %-60s %s", '|'.join(k), self.measures[k])
 | |
| 
 | |
| 
 | |
| class CounterStorage:  # pylint: disable=missing-class-docstring
 | |
| 
 | |
|     __slots__ = 'counters', 'lock'
 | |
| 
 | |
|     def __init__(self):
 | |
|         self.lock = threading.Lock()
 | |
|         self.clear()
 | |
| 
 | |
|     def clear(self):
 | |
|         with self.lock:
 | |
|             self.counters = {}
 | |
| 
 | |
|     def configure(self, *args):
 | |
|         with self.lock:
 | |
|             self.counters[args] = 0
 | |
| 
 | |
|     def get(self, *args):
 | |
|         return self.counters[args]
 | |
| 
 | |
|     def add(self, value, *args):
 | |
|         with self.lock:
 | |
|             self.counters[args] += value
 | |
| 
 | |
|     def dump(self):
 | |
|         with self.lock:
 | |
|             ks = sorted(self.counters.keys(), key='/'.join)  # pylint: disable=invalid-name
 | |
|         logger.debug("Counters:")
 | |
|         for k in ks:
 | |
|             logger.debug("- %-60s %s", '|'.join(k), self.counters[k])
 | |
| 
 | |
| 
 | |
| class VoidHistogram(Histogram):  # pylint: disable=missing-class-docstring
 | |
|     def observe(self, value):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| class VoidCounterStorage(CounterStorage):  # pylint: disable=missing-class-docstring
 | |
|     def add(self, value, *args):
 | |
|         pass
 |