c2cwsgiutils 6.2.0.dev72__py3-none-any.whl → 6.2.0.dev75__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- c2cwsgiutils/__init__.py +4 -4
- c2cwsgiutils/acceptance/__init__.py +4 -1
- c2cwsgiutils/acceptance/connection.py +45 -32
- c2cwsgiutils/acceptance/image.py +45 -39
- c2cwsgiutils/acceptance/print.py +2 -2
- c2cwsgiutils/acceptance/utils.py +4 -4
- c2cwsgiutils/auth.py +20 -13
- c2cwsgiutils/broadcast/__init__.py +25 -16
- c2cwsgiutils/broadcast/interface.py +8 -4
- c2cwsgiutils/broadcast/local.py +9 -4
- c2cwsgiutils/broadcast/redis.py +19 -12
- c2cwsgiutils/client_info.py +3 -2
- c2cwsgiutils/config_utils.py +14 -10
- c2cwsgiutils/coverage_setup.py +5 -5
- c2cwsgiutils/db.py +54 -47
- c2cwsgiutils/db_maintenance_view.py +7 -8
- c2cwsgiutils/debug/__init__.py +1 -2
- c2cwsgiutils/debug/_listeners.py +6 -4
- c2cwsgiutils/debug/_views.py +15 -10
- c2cwsgiutils/debug/utils.py +5 -5
- c2cwsgiutils/errors.py +15 -10
- c2cwsgiutils/health_check.py +79 -60
- c2cwsgiutils/index.py +35 -37
- c2cwsgiutils/loader.py +5 -4
- c2cwsgiutils/logging_view.py +13 -6
- c2cwsgiutils/models_graph.py +5 -5
- c2cwsgiutils/pretty_json.py +3 -2
- c2cwsgiutils/profiler.py +1 -2
- c2cwsgiutils/prometheus.py +6 -6
- c2cwsgiutils/pyramid.py +1 -1
- c2cwsgiutils/pyramid_logging.py +9 -4
- c2cwsgiutils/redis_stats.py +12 -7
- c2cwsgiutils/redis_utils.py +18 -10
- c2cwsgiutils/request_tracking/__init__.py +20 -12
- c2cwsgiutils/request_tracking/_sql.py +2 -1
- c2cwsgiutils/scripts/genversion.py +10 -9
- c2cwsgiutils/scripts/stats_db.py +29 -13
- c2cwsgiutils/sentry.py +44 -20
- c2cwsgiutils/setup_process.py +7 -4
- c2cwsgiutils/sql_profiler/_impl.py +12 -11
- c2cwsgiutils/sqlalchemylogger/_models.py +3 -3
- c2cwsgiutils/sqlalchemylogger/examples/__init__.py +0 -0
- c2cwsgiutils/sqlalchemylogger/handlers.py +8 -9
- c2cwsgiutils/stats_pyramid/_db_spy.py +12 -3
- c2cwsgiutils/stats_pyramid/_pyramid_spy.py +10 -9
- c2cwsgiutils/version.py +10 -7
- {c2cwsgiutils-6.2.0.dev72.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/METADATA +2 -2
- c2cwsgiutils-6.2.0.dev75.dist-info/RECORD +68 -0
- c2cwsgiutils-6.2.0.dev72.dist-info/RECORD +0 -67
- {c2cwsgiutils-6.2.0.dev72.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/LICENSE +0 -0
- {c2cwsgiutils-6.2.0.dev72.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/WHEEL +0 -0
- {c2cwsgiutils-6.2.0.dev72.dist-info → c2cwsgiutils-6.2.0.dev75.dist-info}/entry_points.txt +0 -0
c2cwsgiutils/debug/_views.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
+
import datetime
|
1
2
|
import gc
|
2
3
|
import logging
|
3
4
|
import os
|
4
5
|
import re
|
5
6
|
import time
|
6
|
-
from collections.abc import Mapping
|
7
|
-
from datetime import datetime
|
7
|
+
from collections.abc import Callable, Mapping
|
8
8
|
from io import StringIO
|
9
|
-
from typing import Any,
|
9
|
+
from typing import Any, cast
|
10
10
|
|
11
11
|
import objgraph
|
12
12
|
import psutil
|
@@ -79,7 +79,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
|
|
79
79
|
try:
|
80
80
|
if request.params.get("no_warmup", "0").lower() in ("1", "true", "on"):
|
81
81
|
request.invoke_subrequest(sub_request)
|
82
|
-
except Exception: # pylint: disable=broad-
|
82
|
+
except Exception: # pylint: disable=broad-exception-caught
|
83
83
|
pass
|
84
84
|
|
85
85
|
_LOG.debug("checking memory growth for %s", path)
|
@@ -119,7 +119,7 @@ def _dump_memory_diff(request: pyramid.request.Request) -> list[Any]:
|
|
119
119
|
"dirty_kb": (mem_after.dirty - mem_before.dirty) / 1024,
|
120
120
|
},
|
121
121
|
"elapsed_time": elapsed_time,
|
122
|
-
"objgraph.growth": objgraph.growth(limit=limit, peak_stats=peak_stats, shortnames=False), # type: ignore
|
122
|
+
"objgraph.growth": objgraph.growth(limit=limit, peak_stats=peak_stats, shortnames=False), # type: ignore[return-value]
|
123
123
|
}
|
124
124
|
|
125
125
|
|
@@ -162,18 +162,23 @@ def _error(request: pyramid.request.Request) -> Any:
|
|
162
162
|
def _time(request: pyramid.request.Request) -> Any:
|
163
163
|
del request # unused
|
164
164
|
return {
|
165
|
-
"local_time": str(datetime.now()),
|
166
|
-
"gmt_time": str(datetime.
|
165
|
+
"local_time": str(datetime.datetime.now()), # noqa: DTZ005
|
166
|
+
"gmt_time": str(datetime.datetime.now(datetime.timezone.utc)),
|
167
167
|
"epoch": time.time(),
|
168
|
-
"timezone": datetime.now().astimezone().tzname(),
|
168
|
+
"timezone": datetime.datetime.now().astimezone().tzname(),
|
169
169
|
}
|
170
170
|
|
171
171
|
|
172
172
|
def _add_view(
|
173
|
-
config: pyramid.config.Configurator,
|
173
|
+
config: pyramid.config.Configurator,
|
174
|
+
name: str,
|
175
|
+
path: str,
|
176
|
+
view: Callable[[pyramid.request.Request], Any],
|
174
177
|
) -> None:
|
175
178
|
config.add_route(
|
176
|
-
"c2c_debug_" + name,
|
179
|
+
"c2c_debug_" + name,
|
180
|
+
config_utils.get_base_path(config) + r"/debug/" + path,
|
181
|
+
request_method="GET",
|
177
182
|
)
|
178
183
|
config.add_view(view, route_name="c2c_debug_" + name, renderer="fast_json", http_cache=0)
|
179
184
|
|
c2cwsgiutils/debug/utils.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import gc
|
2
2
|
import logging
|
3
|
-
import os
|
4
3
|
import re
|
5
4
|
import sys
|
6
5
|
from collections import defaultdict
|
6
|
+
from pathlib import Path
|
7
7
|
from types import FunctionType, ModuleType
|
8
8
|
from typing import Any
|
9
9
|
|
@@ -37,14 +37,14 @@ def get_size(obj: Any) -> int:
|
|
37
37
|
|
38
38
|
def dump_memory_maps(pid: str = "self") -> list[dict[str, Any]]:
|
39
39
|
"""Get the Linux memory maps."""
|
40
|
-
filename =
|
41
|
-
if not
|
40
|
+
filename = Path("/proc") / pid / "smaps"
|
41
|
+
if not filename.exists():
|
42
42
|
return []
|
43
|
-
with open(
|
43
|
+
with filename.open(encoding="utf-8") as input_:
|
44
44
|
cur_dict: dict[str, int] = defaultdict(int)
|
45
45
|
sizes: dict[str, Any] = {}
|
46
46
|
for line in input_:
|
47
|
-
line = line.rstrip("\n")
|
47
|
+
line = line.rstrip("\n") # noqa: PLW2901
|
48
48
|
matcher = _SMAPS_LOCATION_RE.match(line)
|
49
49
|
if matcher:
|
50
50
|
cur_dict = sizes.setdefault(matcher.group(1), defaultdict(int))
|
c2cwsgiutils/errors.py
CHANGED
@@ -4,7 +4,8 @@ import logging
|
|
4
4
|
import os
|
5
5
|
import traceback
|
6
6
|
import warnings
|
7
|
-
from
|
7
|
+
from collections.abc import Callable
|
8
|
+
from typing import Any
|
8
9
|
|
9
10
|
import pyramid.request
|
10
11
|
import sqlalchemy.exc
|
@@ -34,7 +35,7 @@ def _crude_add_cors(request: pyramid.request.Request, response: pyramid.response
|
|
34
35
|
response = request.response
|
35
36
|
response.headers["Access-Control-Allow-Origin"] = "*"
|
36
37
|
response.headers["Access-Control-Allow-Methods"] = ",".join(
|
37
|
-
{request.headers.get("Access-Control-Request-Method", request.method)} | {"OPTIONS", "HEAD"}
|
38
|
+
{request.headers.get("Access-Control-Request-Method", request.method)} | {"OPTIONS", "HEAD"},
|
38
39
|
)
|
39
40
|
response.headers["Access-Control-Allow-Headers"] = "session-id"
|
40
41
|
response.headers["Access-Control-Max-Age"] = "86400"
|
@@ -57,7 +58,7 @@ def _do_error(
|
|
57
58
|
status: int,
|
58
59
|
exception: Exception,
|
59
60
|
logger: Callable[..., None] = _LOG.error,
|
60
|
-
reduce_info_sent: Callable[[Exception], None] = lambda e: None,
|
61
|
+
reduce_info_sent: Callable[[Exception], None] = lambda e: None, # noqa: ARG005
|
61
62
|
) -> pyramid.response.Response:
|
62
63
|
logger(
|
63
64
|
"%s %s returned status code %s",
|
@@ -98,10 +99,9 @@ def _http_error(exception: HTTPException, request: pyramid.request.Request) -> A
|
|
98
99
|
_add_cors(request)
|
99
100
|
request.response.status_code = exception.status_code
|
100
101
|
return {"message": str(exception), "status": exception.status_code}
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
return request.response
|
102
|
+
_crude_add_cors(request)
|
103
|
+
request.response.status_code = 200
|
104
|
+
return request.response
|
105
105
|
|
106
106
|
|
107
107
|
def _include_dev_details(request: pyramid.request.Request) -> bool:
|
@@ -109,7 +109,8 @@ def _include_dev_details(request: pyramid.request.Request) -> bool:
|
|
109
109
|
|
110
110
|
|
111
111
|
def _integrity_error(
|
112
|
-
exception: sqlalchemy.exc.StatementError,
|
112
|
+
exception: sqlalchemy.exc.StatementError,
|
113
|
+
request: pyramid.request.Request,
|
113
114
|
) -> pyramid.response.Response:
|
114
115
|
def reduce_info_sent(e: Exception) -> None:
|
115
116
|
if isinstance(e, sqlalchemy.exc.StatementError):
|
@@ -121,7 +122,8 @@ def _integrity_error(
|
|
121
122
|
|
122
123
|
|
123
124
|
def _client_interrupted_error(
|
124
|
-
exception: Exception,
|
125
|
+
exception: Exception,
|
126
|
+
request: pyramid.request.Request,
|
125
127
|
) -> pyramid.response.Response:
|
126
128
|
# No need to cry wolf if it's just the client that interrupted the connection
|
127
129
|
return _do_error(request, 500, exception, logger=_LOG.info)
|
@@ -165,7 +167,10 @@ def includeme(config: pyramid.config.Configurator) -> None:
|
|
165
167
|
"""Initialize the error views."""
|
166
168
|
if (
|
167
169
|
config_utils.env_or_config(
|
168
|
-
config,
|
170
|
+
config,
|
171
|
+
"C2C_ENABLE_EXCEPTION_HANDLING",
|
172
|
+
"c2c.enable_exception_handling",
|
173
|
+
"0",
|
169
174
|
)
|
170
175
|
!= "0"
|
171
176
|
):
|
c2cwsgiutils/health_check.py
CHANGED
@@ -13,10 +13,11 @@ import re
|
|
13
13
|
import subprocess # nosec
|
14
14
|
import time
|
15
15
|
import traceback
|
16
|
-
from collections.abc import Mapping
|
16
|
+
from collections.abc import Callable, Mapping
|
17
17
|
from enum import Enum
|
18
|
+
from pathlib import Path
|
18
19
|
from types import TracebackType
|
19
|
-
from typing import Any,
|
20
|
+
from typing import Any, Literal, cast
|
20
21
|
|
21
22
|
import prometheus_client
|
22
23
|
import pyramid.config
|
@@ -63,10 +64,10 @@ class EngineType(Enum):
|
|
63
64
|
READ_AND_WRITE = 3
|
64
65
|
|
65
66
|
|
66
|
-
class JsonCheckException(Exception):
|
67
|
+
class JsonCheckException(Exception): # noqa: N818
|
67
68
|
"""Checker exception used to add some structured content to a failure."""
|
68
69
|
|
69
|
-
def __init__(self, message: str, json: Any):
|
70
|
+
def __init__(self, message: str, json: Any) -> None:
|
70
71
|
"""Initialize the exception."""
|
71
72
|
super().__init__()
|
72
73
|
self.message = message
|
@@ -83,22 +84,22 @@ class JsonCheckException(Exception):
|
|
83
84
|
class _Binding:
|
84
85
|
def name(self) -> str:
|
85
86
|
"""Return the name of the binding."""
|
86
|
-
raise NotImplementedError
|
87
|
+
raise NotImplementedError
|
87
88
|
|
88
89
|
def __enter__(self) -> _scoped_session:
|
89
|
-
raise NotImplementedError
|
90
|
+
raise NotImplementedError
|
90
91
|
|
91
92
|
def __exit__(
|
92
93
|
self,
|
93
|
-
exc_type:
|
94
|
-
exc_value:
|
95
|
-
exc_traceback:
|
94
|
+
exc_type: type[BaseException] | None,
|
95
|
+
exc_value: BaseException | None,
|
96
|
+
exc_traceback: TracebackType | None,
|
96
97
|
) -> Literal[False]:
|
97
98
|
return False
|
98
99
|
|
99
100
|
|
100
101
|
class _NewBinding(_Binding):
|
101
|
-
def __init__(self, session: c2cwsgiutils.db.SessionFactory, readwrite: bool):
|
102
|
+
def __init__(self, session: c2cwsgiutils.db.SessionFactory, readwrite: bool) -> None:
|
102
103
|
self.session = session
|
103
104
|
self.readwrite = readwrite
|
104
105
|
|
@@ -110,84 +111,85 @@ class _NewBinding(_Binding):
|
|
110
111
|
|
111
112
|
|
112
113
|
class _OldBinding(_Binding):
|
113
|
-
def __init__(self, session: _scoped_session, engine: sqlalchemy.engine.Engine):
|
114
|
+
def __init__(self, session: _scoped_session, engine: sqlalchemy.engine.Engine) -> None:
|
114
115
|
self.session = session
|
115
116
|
self.engine = engine
|
116
117
|
self.prev_bind = None
|
117
118
|
|
118
119
|
def name(self) -> str:
|
119
|
-
return cast(str, self.engine.c2c_name) # type: ignore
|
120
|
+
return cast(str, self.engine.c2c_name) # type: ignore[attr-defined]
|
120
121
|
|
121
122
|
def __enter__(self) -> _scoped_session:
|
122
|
-
self.prev_bind = self.session.bind # type: ignore
|
123
|
+
self.prev_bind = self.session.bind # type: ignore[assignment]
|
123
124
|
self.session.bind = self.engine
|
124
125
|
return self.session
|
125
126
|
|
126
127
|
def __exit__(
|
127
128
|
self,
|
128
|
-
exc_type:
|
129
|
-
exc_value:
|
130
|
-
exc_traceback:
|
129
|
+
exc_type: type[BaseException] | None,
|
130
|
+
exc_value: BaseException | None,
|
131
|
+
exc_traceback: TracebackType | None,
|
131
132
|
) -> Literal[False]:
|
132
133
|
self.session.bind = self.prev_bind
|
133
134
|
return False
|
134
135
|
|
135
136
|
|
136
137
|
def _get_binding_class(
|
137
|
-
session:
|
138
|
+
session: _scoped_session | c2cwsgiutils.db.SessionFactory,
|
138
139
|
ro_engin: sqlalchemy.engine.Engine,
|
139
140
|
rw_engin: sqlalchemy.engine.Engine,
|
140
141
|
readwrite: bool,
|
141
142
|
) -> _Binding:
|
142
143
|
if isinstance(session, c2cwsgiutils.db.SessionFactory):
|
143
144
|
return _NewBinding(session, readwrite)
|
144
|
-
else
|
145
|
-
return _OldBinding(session, ro_engin if readwrite else rw_engin)
|
145
|
+
return _OldBinding(session, ro_engin if readwrite else rw_engin)
|
146
146
|
|
147
147
|
|
148
148
|
def _get_bindings(
|
149
|
-
session:
|
149
|
+
session: _scoped_session | c2cwsgiutils.db.SessionFactory,
|
150
150
|
engine_type: EngineType,
|
151
151
|
) -> list[_Binding]:
|
152
152
|
if isinstance(session, c2cwsgiutils.db.SessionFactory):
|
153
153
|
ro_engin = session.ro_engine
|
154
154
|
rw_engin = session.rw_engine
|
155
155
|
else:
|
156
|
-
ro_engin = session.c2c_ro_bind # type: ignore
|
157
|
-
rw_engin = session.c2c_rw_bind # type: ignore
|
156
|
+
ro_engin = session.c2c_ro_bind # type: ignore[attr-defined]
|
157
|
+
rw_engin = session.c2c_rw_bind # type: ignore[attr-defined]
|
158
158
|
|
159
159
|
if rw_engin == ro_engin:
|
160
160
|
engine_type = EngineType.WRITE_ONLY
|
161
161
|
|
162
162
|
if engine_type == EngineType.READ_AND_WRITE:
|
163
163
|
return [
|
164
|
-
_get_binding_class(session, ro_engin, rw_engin, False),
|
165
|
-
_get_binding_class(session, ro_engin, rw_engin, True),
|
164
|
+
_get_binding_class(session, ro_engin, rw_engin, readwrite=False),
|
165
|
+
_get_binding_class(session, ro_engin, rw_engin, readwrite=True),
|
166
166
|
]
|
167
167
|
if engine_type == EngineType.READ_ONLY:
|
168
|
-
return [_get_binding_class(session, ro_engin, ro_engin, False)]
|
168
|
+
return [_get_binding_class(session, ro_engin, ro_engin, readwrite=False)]
|
169
169
|
if engine_type == EngineType.WRITE_ONLY:
|
170
|
-
return [_get_binding_class(session, rw_engin, rw_engin, True)]
|
170
|
+
return [_get_binding_class(session, rw_engin, rw_engin, readwrite=True)]
|
171
171
|
|
172
172
|
raise NotImplementedError(f"Unhandled engine type {engine_type}")
|
173
173
|
|
174
174
|
|
175
|
-
def _get_alembic_version(alembic_ini_path:
|
175
|
+
def _get_alembic_version(alembic_ini_path: Path, name: str) -> str:
|
176
176
|
# Go to the directory holding the config file and add '.' to the PYTHONPATH variable to support Alembic
|
177
177
|
# migration scripts using common modules
|
178
178
|
env = dict(os.environ)
|
179
179
|
pythonpath = os.environ.get("PYTHONPATH", "")
|
180
180
|
pythonpath = (pythonpath + ":" if pythonpath else "") + "."
|
181
181
|
env["PYTHONPATH"] = pythonpath
|
182
|
-
dirname =
|
182
|
+
dirname = alembic_ini_path.parent.resolve()
|
183
183
|
|
184
184
|
out = subprocess.check_output( # nosec
|
185
|
-
["alembic", "--config", alembic_ini_path, "--name", name, "heads"],
|
185
|
+
["alembic", "--config", alembic_ini_path, "--name", name, "heads"],
|
186
|
+
cwd=dirname,
|
187
|
+
env=env,
|
186
188
|
).decode("utf-8")
|
187
189
|
out_match = _ALEMBIC_HEAD_RE.match(out)
|
188
190
|
if not out_match:
|
189
191
|
raise Exception( # pylint: disable=broad-exception-raised
|
190
|
-
"Cannot get the alembic HEAD version from: " + out
|
192
|
+
"Cannot get the alembic HEAD version from: " + out,
|
191
193
|
)
|
192
194
|
return out_match.group(1)
|
193
195
|
|
@@ -202,7 +204,9 @@ class HealthCheck:
|
|
202
204
|
def __init__(self, config: pyramid.config.Configurator) -> None:
|
203
205
|
"""Initialize the health check view."""
|
204
206
|
config.add_route(
|
205
|
-
"c2c_health_check",
|
207
|
+
"c2c_health_check",
|
208
|
+
config_utils.get_base_path(config) + r"/health_check",
|
209
|
+
request_method="GET",
|
206
210
|
)
|
207
211
|
config.add_view(self._view, route_name="c2c_health_check", renderer="fast_json", http_cache=0)
|
208
212
|
self._checks: list[tuple[str, Callable[[pyramid.request.Request], Any], int]] = []
|
@@ -220,9 +224,9 @@ class HealthCheck:
|
|
220
224
|
|
221
225
|
def add_db_session_check(
|
222
226
|
self,
|
223
|
-
session:
|
224
|
-
query_cb:
|
225
|
-
at_least_one_model:
|
227
|
+
session: _scoped_session | c2cwsgiutils.db.SessionFactory,
|
228
|
+
query_cb: Callable[[_scoped_session], Any] | None = None,
|
229
|
+
at_least_one_model: object | None = None,
|
226
230
|
level: int = 1,
|
227
231
|
engine_type: EngineType = EngineType.READ_AND_WRITE,
|
228
232
|
) -> None:
|
@@ -249,11 +253,11 @@ class HealthCheck:
|
|
249
253
|
def add_alembic_check(
|
250
254
|
self,
|
251
255
|
session: _scoped_session,
|
252
|
-
alembic_ini_path: str,
|
256
|
+
alembic_ini_path: Path | str,
|
253
257
|
level: int = 2,
|
254
258
|
name: str = "alembic",
|
255
|
-
version_schema:
|
256
|
-
version_table:
|
259
|
+
version_schema: str | None = None,
|
260
|
+
version_table: str | None = None,
|
257
261
|
) -> None:
|
258
262
|
"""
|
259
263
|
Check the DB version against the HEAD version of Alembic.
|
@@ -270,6 +274,8 @@ class HealthCheck:
|
|
270
274
|
ro_engin: the RO engine to use (if None, use the session one)
|
271
275
|
|
272
276
|
"""
|
277
|
+
if isinstance(alembic_ini_path, str):
|
278
|
+
alembic_ini_path = Path(alembic_ini_path)
|
273
279
|
version_ = _get_alembic_version(alembic_ini_path, name)
|
274
280
|
|
275
281
|
config = configparser.ConfigParser()
|
@@ -288,47 +294,55 @@ class HealthCheck:
|
|
288
294
|
self.session = session
|
289
295
|
|
290
296
|
def __call__(self, request: pyramid.request.Request) -> str:
|
297
|
+
del request # Unused
|
291
298
|
assert version_schema
|
292
299
|
assert version_table
|
293
300
|
for binding in _get_bindings(self.session, EngineType.READ_AND_WRITE):
|
294
301
|
with (
|
295
302
|
binding as binded_session,
|
296
303
|
_PROMETHEUS_DB_SUMMARY.labels(
|
297
|
-
configuration=alembic_ini_path,
|
304
|
+
configuration=alembic_ini_path,
|
305
|
+
connection=binding.name(),
|
306
|
+
check="alembic",
|
298
307
|
).time(),
|
299
308
|
):
|
300
309
|
result = binded_session.execute(
|
301
310
|
sqlalchemy.text(
|
302
311
|
"SELECT version_num FROM " # noqa: S608 # nosec
|
303
|
-
f"{sqlalchemy.sql.quoted_name(version_schema, True)}."
|
304
|
-
f"{sqlalchemy.sql.quoted_name(version_table, True)}"
|
305
|
-
)
|
312
|
+
f"{sqlalchemy.sql.quoted_name(version_schema, quote=True)}."
|
313
|
+
f"{sqlalchemy.sql.quoted_name(version_table, quote=True)}",
|
314
|
+
),
|
306
315
|
).fetchone()
|
307
316
|
assert result is not None
|
308
317
|
(actual_version,) = result
|
309
318
|
_PROMETHEUS_ALEMBIC_VERSION.labels(
|
310
|
-
version=actual_version,
|
319
|
+
version=actual_version,
|
320
|
+
name=name,
|
321
|
+
configuration=alembic_ini_path,
|
311
322
|
).set(1)
|
312
323
|
if actual_version != version_:
|
313
|
-
|
324
|
+
error_message = (
|
314
325
|
f"Invalid alembic version (db: {actual_version}, code: {version_})"
|
315
326
|
)
|
327
|
+
raise Exception(error_message) # pylint: disable=broad-exception-raised
|
316
328
|
return version_
|
317
329
|
|
318
330
|
self._checks.append(
|
319
|
-
(
|
331
|
+
(
|
332
|
+
"alembic_" + str(alembic_ini_path).replace("/", "_").strip("_") + "_" + name,
|
333
|
+
_Check(session),
|
334
|
+
level,
|
335
|
+
),
|
320
336
|
)
|
321
337
|
|
322
338
|
def add_url_check(
|
323
339
|
self,
|
324
|
-
url:
|
325
|
-
params:
|
326
|
-
headers:
|
327
|
-
|
328
|
-
] =
|
329
|
-
|
330
|
-
check_cb: Callable[[pyramid.request.Request, requests.Response], Any] = lambda request,
|
331
|
-
response: None,
|
340
|
+
url: str | Callable[[pyramid.request.Request], str],
|
341
|
+
params: Mapping[str, str] | Callable[[pyramid.request.Request], Mapping[str, str]] | None = None,
|
342
|
+
headers: Mapping[str, str] | Callable[[pyramid.request.Request], Mapping[str, str]] | None = None,
|
343
|
+
name: str | None = None,
|
344
|
+
check_cb: Callable[[pyramid.request.Request, requests.Response], Any] = lambda request, # noqa: ARG005
|
345
|
+
response: None, # noqa: ARG005
|
332
346
|
timeout: float = 3,
|
333
347
|
level: int = 1,
|
334
348
|
) -> None:
|
@@ -344,7 +358,6 @@ class HealthCheck:
|
|
344
358
|
response as parameters)
|
345
359
|
timeout: the timeout
|
346
360
|
level: the level of the health check
|
347
|
-
|
348
361
|
"""
|
349
362
|
|
350
363
|
def check(request: pyramid.request.Request) -> Any:
|
@@ -362,7 +375,7 @@ class HealthCheck:
|
|
362
375
|
assert name
|
363
376
|
self._checks.append((name, check, level))
|
364
377
|
|
365
|
-
def add_redis_check(self, name:
|
378
|
+
def add_redis_check(self, name: str | None = None, level: int = 1) -> None:
|
366
379
|
"""
|
367
380
|
Check that the given redis server is reachable.
|
368
381
|
|
@@ -382,7 +395,7 @@ class HealthCheck:
|
|
382
395
|
def add(name: str, func: Callable[..., Any], *args: Any) -> None:
|
383
396
|
try:
|
384
397
|
result[name] = func(*args)
|
385
|
-
except Exception as e: # pylint: disable=broad-
|
398
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
386
399
|
result[name] = {"error": str(e)}
|
387
400
|
|
388
401
|
if master is not None:
|
@@ -409,7 +422,8 @@ class HealthCheck:
|
|
409
422
|
name = self.name
|
410
423
|
|
411
424
|
if name is None:
|
412
|
-
|
425
|
+
exception_message = "Redis should be configured"
|
426
|
+
raise RuntimeError(exception_message)
|
413
427
|
|
414
428
|
self._checks.append((name, check, level))
|
415
429
|
|
@@ -437,7 +451,10 @@ class HealthCheck:
|
|
437
451
|
self._checks.append((name, check, level))
|
438
452
|
|
439
453
|
def add_custom_check(
|
440
|
-
self,
|
454
|
+
self,
|
455
|
+
name: str,
|
456
|
+
check_cb: Callable[[pyramid.request.Request], Any],
|
457
|
+
level: int = 1,
|
441
458
|
) -> None:
|
442
459
|
"""
|
443
460
|
Add a custom check.
|
@@ -489,7 +506,7 @@ class HealthCheck:
|
|
489
506
|
if result is not None:
|
490
507
|
results["successes"][name]["result"] = result
|
491
508
|
_set_success(check_name=name)
|
492
|
-
except Exception as e: # pylint: disable=broad-
|
509
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
493
510
|
_PROMETHEUS_HEALTH_CHECKS_FAILURE.labels(name=name).set(1)
|
494
511
|
_LOG.warning("Health check %s failed", name, exc_info=True)
|
495
512
|
failure = {"message": str(e), "timing": time.perf_counter() - start, "level": level}
|
@@ -509,7 +526,9 @@ class HealthCheck:
|
|
509
526
|
with (
|
510
527
|
binding as session,
|
511
528
|
_PROMETHEUS_DB_SUMMARY.labels(
|
512
|
-
connection=binding.name(),
|
529
|
+
connection=binding.name(),
|
530
|
+
check="database",
|
531
|
+
configuration="<default>",
|
513
532
|
).time(),
|
514
533
|
):
|
515
534
|
return query_cb(session)
|
@@ -537,5 +556,5 @@ def _set_success(check_name: str) -> None:
|
|
537
556
|
|
538
557
|
|
539
558
|
@broadcast.decorator(expect_answers=True)
|
540
|
-
def _get_all_versions() ->
|
559
|
+
def _get_all_versions() -> str | None:
|
541
560
|
return version.get_version()
|