.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
__init__.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
5import math
6import contextlib
7from timeit import default_timer
8from operator import itemgetter
9
10from searx.engines import engines
11from .models import HistogramStorage, CounterStorage, VoidHistogram, VoidCounterStorage
12from .error_recorder import count_error, count_exception, errors_per_engines
13
14__all__ = [
15 "initialize",
16 "get_engines_stats",
17 "get_engine_errors",
18 "histogram",
19 "histogram_observe",
20 "histogram_observe_time",
21 "counter",
22 "counter_inc",
23 "counter_add",
24 "count_error",
25 "count_exception",
26]
27
28
29ENDPOINTS = {'search'}
30
31
32histogram_storage: typing.Optional[HistogramStorage] = None
33counter_storage: typing.Optional[CounterStorage] = None
34
35
36@contextlib.contextmanager
38 h = histogram_storage.get(*args)
39 before = default_timer()
40 yield before
41 duration = default_timer() - before
42 if h:
43 h.observe(duration)
44 else:
45 raise ValueError("histogram " + repr((*args,)) + " doesn't not exist")
46
47
48def histogram_observe(duration, *args):
49 histogram_storage.get(*args).observe(duration)
50
51
52def histogram(*args, raise_on_not_found=True):
53 h = histogram_storage.get(*args)
54 if raise_on_not_found and h is None:
55 raise ValueError("histogram " + repr((*args,)) + " doesn't not exist")
56 return h
57
58
59def counter_inc(*args):
60 counter_storage.add(1, *args)
61
62
63def counter_add(value, *args):
64 counter_storage.add(value, *args)
65
66
67def counter(*args):
68 return counter_storage.get(*args)
69
70
71def initialize(engine_names=None, enabled=True):
72 """
73 Initialize metrics
74 """
75 global counter_storage, histogram_storage # pylint: disable=global-statement
76
77 if enabled:
78 counter_storage = CounterStorage()
79 histogram_storage = HistogramStorage()
80 else:
81 counter_storage = VoidCounterStorage()
82 histogram_storage = HistogramStorage(histogram_class=VoidHistogram)
83
84 # max_timeout = max of all the engine.timeout
85 max_timeout = 2
86 for engine_name in engine_names or engines:
87 if engine_name in engines:
88 max_timeout = max(max_timeout, engines[engine_name].timeout)
89
90 # histogram configuration
91 histogram_width = 0.1
92 histogram_size = int(1.5 * max_timeout / histogram_width)
93
94 # engines
95 for engine_name in engine_names or engines:
96 # search count
97 counter_storage.configure('engine', engine_name, 'search', 'count', 'sent')
98 counter_storage.configure('engine', engine_name, 'search', 'count', 'successful')
99 # global counter of errors
100 counter_storage.configure('engine', engine_name, 'search', 'count', 'error')
101 # score of the engine
102 counter_storage.configure('engine', engine_name, 'score')
103 # result count per requests
104 histogram_storage.configure(1, 100, 'engine', engine_name, 'result', 'count')
105 # time doing HTTP requests
106 histogram_storage.configure(histogram_width, histogram_size, 'engine', engine_name, 'time', 'http')
107 # total time
108 # .time.request and ...response times may overlap .time.http time.
109 histogram_storage.configure(histogram_width, histogram_size, 'engine', engine_name, 'time', 'total')
110
111
112def get_engine_errors(engline_name_list):
113 result = {}
114 engine_names = list(errors_per_engines.keys())
115 engine_names.sort()
116 for engine_name in engine_names:
117 if engine_name not in engline_name_list:
118 continue
119
120 error_stats = errors_per_engines[engine_name]
121 sent_search_count = max(counter('engine', engine_name, 'search', 'count', 'sent'), 1)
122 sorted_context_count_list = sorted(error_stats.items(), key=lambda context_count: context_count[1])
123 r = []
124 for context, count in sorted_context_count_list:
125 percentage = round(20 * count / sent_search_count) * 5
126 r.append(
127 {
128 'filename': context.filename,
129 'function': context.function,
130 'line_no': context.line_no,
131 'code': context.code,
132 'exception_classname': context.exception_classname,
133 'log_message': context.log_message,
134 'log_parameters': context.log_parameters,
135 'secondary': context.secondary,
136 'percentage': percentage,
137 }
138 )
139 result[engine_name] = sorted(r, reverse=True, key=lambda d: d['percentage'])
140 return result
141
142
143def get_reliabilities(engline_name_list, checker_results):
144 reliabilities = {}
145
146 engine_errors = get_engine_errors(engline_name_list)
147
148 for engine_name in engline_name_list:
149 checker_result = checker_results.get(engine_name, {})
150 checker_success = checker_result.get('success', True)
151 errors = engine_errors.get(engine_name) or []
152 if counter('engine', engine_name, 'search', 'count', 'sent') == 0:
153 # no request
154 reliability = None
155 elif checker_success and not errors:
156 reliability = 100
157 elif 'simple' in checker_result.get('errors', {}):
158 # the basic (simple) test doesn't work: the engine is broken according to the checker
159 # even if there is no exception
160 reliability = 0
161 else:
162 # pylint: disable=consider-using-generator
163 reliability = 100 - sum([error['percentage'] for error in errors if not error.get('secondary')])
164
165 reliabilities[engine_name] = {
166 'reliability': reliability,
167 'errors': errors,
168 'checker': checker_results.get(engine_name, {}).get('errors', {}),
169 }
170 return reliabilities
171
172
173def get_engines_stats(engine_name_list):
174 assert counter_storage is not None
175 assert histogram_storage is not None
176
177 list_time = []
178 max_time_total = max_result_count = None
179
180 for engine_name in engine_name_list:
181
182 sent_count = counter('engine', engine_name, 'search', 'count', 'sent')
183 if sent_count == 0:
184 continue
185
186 result_count = histogram('engine', engine_name, 'result', 'count').percentage(50)
187 result_count_sum = histogram('engine', engine_name, 'result', 'count').sum
188 successful_count = counter('engine', engine_name, 'search', 'count', 'successful')
189
190 time_total = histogram('engine', engine_name, 'time', 'total').percentage(50)
191 max_time_total = max(time_total or 0, max_time_total or 0)
192 max_result_count = max(result_count or 0, max_result_count or 0)
193
194 stats = {
195 'name': engine_name,
196 'total': None,
197 'total_p80': None,
198 'total_p95': None,
199 'http': None,
200 'http_p80': None,
201 'http_p95': None,
202 'processing': None,
203 'processing_p80': None,
204 'processing_p95': None,
205 'score': 0,
206 'score_per_result': 0,
207 'result_count': result_count,
208 }
209
210 if successful_count and result_count_sum:
211 score = counter('engine', engine_name, 'score')
212
213 stats['score'] = score
214 stats['score_per_result'] = score / float(result_count_sum)
215
216 time_http = histogram('engine', engine_name, 'time', 'http').percentage(50)
217 time_http_p80 = time_http_p95 = 0
218
219 if time_http is not None:
220
221 time_http_p80 = histogram('engine', engine_name, 'time', 'http').percentage(80)
222 time_http_p95 = histogram('engine', engine_name, 'time', 'http').percentage(95)
223
224 stats['http'] = round(time_http, 1)
225 stats['http_p80'] = round(time_http_p80, 1)
226 stats['http_p95'] = round(time_http_p95, 1)
227
228 if time_total is not None:
229
230 time_total_p80 = histogram('engine', engine_name, 'time', 'total').percentage(80)
231 time_total_p95 = histogram('engine', engine_name, 'time', 'total').percentage(95)
232
233 stats['total'] = round(time_total, 1)
234 stats['total_p80'] = round(time_total_p80, 1)
235 stats['total_p95'] = round(time_total_p95, 1)
236
237 stats['processing'] = round(time_total - (time_http or 0), 1)
238 stats['processing_p80'] = round(time_total_p80 - time_http_p80, 1)
239 stats['processing_p95'] = round(time_total_p95 - time_http_p95, 1)
240
241 list_time.append(stats)
242
243 return {
244 'time': list_time,
245 'max_time': math.ceil(max_time_total or 0),
246 'max_result_count': math.ceil(max_result_count or 0),
247 }
::1337x
Definition 1337x.py:1
get_engine_errors(engline_name_list)
Definition __init__.py:112
counter_add(value, *args)
Definition __init__.py:63
histogram(*args, raise_on_not_found=True)
Definition __init__.py:52
counter(*args)
Definition __init__.py:67
get_engines_stats(engine_name_list)
Definition __init__.py:173
histogram_observe(duration, *args)
Definition __init__.py:48
initialize(engine_names=None, enabled=True)
Definition __init__.py:71
get_reliabilities(engline_name_list, checker_results)
Definition __init__.py:143
counter_inc(*args)
Definition __init__.py:59
histogram_observe_time(*args)
Definition __init__.py:37