c2cwsgiutils 5.2.1__tar.gz → 5.2.1.dev197__tar.gz
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-5.2.1 → c2cwsgiutils-5.2.1.dev197}/PKG-INFO +168 -99
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/README.md +140 -74
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/__init__.py +12 -12
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/acceptance/connection.py +5 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/acceptance/image.py +95 -3
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/acceptance/package-lock.json +1933 -0
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/acceptance/package.json +7 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/acceptance/print.py +3 -3
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/acceptance/screenshot.js +62 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/acceptance/utils.py +14 -22
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/auth.py +4 -4
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/broadcast/__init__.py +15 -7
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/broadcast/interface.py +3 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/broadcast/local.py +3 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/broadcast/redis.py +6 -5
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/client_info.py +5 -5
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/config_utils.py +2 -1
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/db.py +20 -11
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/db_maintenance_view.py +2 -1
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/debug/_listeners.py +7 -6
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/debug/_views.py +11 -10
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/debug/utils.py +5 -5
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/health_check.py +72 -73
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/index.py +90 -105
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/loader.py +3 -3
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/logging_view.py +3 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/models_graph.py +4 -4
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/prometheus.py +184 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/pyramid.py +4 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/pyramid_logging.py +2 -1
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/redis_stats.py +13 -11
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/redis_utils.py +11 -5
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/request_tracking/__init__.py +36 -30
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/scripts/genversion.py +4 -4
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/scripts/stats_db.py +92 -60
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sentry.py +2 -1
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/setup_process.py +12 -16
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sql_profiler/_impl.py +3 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sqlalchemylogger/_models.py +2 -2
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sqlalchemylogger/handlers.py +6 -6
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/static/favicon-16x16.png +0 -0
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/static/favicon-32x32.png +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/stats_pyramid/__init__.py +7 -11
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/stats_pyramid/_db_spy.py +14 -11
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/stats_pyramid/_pyramid_spy.py +27 -21
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/templates/index.html.mako +50 -0
- c2cwsgiutils-5.2.1.dev197/c2cwsgiutils/version.py +90 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/pyproject.toml +74 -77
- c2cwsgiutils-5.2.1/c2cwsgiutils/acceptance/composition.py +0 -129
- c2cwsgiutils-5.2.1/c2cwsgiutils/metrics.py +0 -110
- c2cwsgiutils-5.2.1/c2cwsgiutils/prometheus.py +0 -66
- c2cwsgiutils-5.2.1/c2cwsgiutils/scripts/check_es.py +0 -130
- c2cwsgiutils-5.2.1/c2cwsgiutils/stats.py +0 -344
- c2cwsgiutils-5.2.1/c2cwsgiutils/stats_pyramid/_views.py +0 -16
- c2cwsgiutils-5.2.1/c2cwsgiutils/version.py +0 -57
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/LICENSE +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/acceptance/__init__.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/broadcast/utils.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/coverage_setup.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/debug/__init__.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/errors.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/pretty_json.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/profiler.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/py.typed +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/request_tracking/_sql.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/scripts/__init__.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/scripts/test_print.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/services.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sql_profiler/__init__.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sqlalchemylogger/README.md +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sqlalchemylogger/__init__.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sqlalchemylogger/_filters.py +0 -0
- {c2cwsgiutils-5.2.1 → c2cwsgiutils-5.2.1.dev197}/c2cwsgiutils/sqlalchemylogger/examples/example.py +0 -0
@@ -1,13 +1,13 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: c2cwsgiutils
|
3
|
-
Version: 5.2.1
|
3
|
+
Version: 5.2.1.dev197
|
4
4
|
Summary: Common utilities for Camptocamp WSGI applications
|
5
5
|
Home-page: https://github.com/camptocamp/c2cwsgiutils
|
6
6
|
License: BSD-2-Clause
|
7
7
|
Keywords: geo,gis,sqlalchemy,orm,wsgi
|
8
8
|
Author: Camptocamp
|
9
9
|
Author-email: info@camptocamp.com
|
10
|
-
Requires-Python: >=3.
|
10
|
+
Requires-Python: >=3.9,<4.0
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
12
12
|
Classifier: Environment :: Plugins
|
13
13
|
Classifier: Framework :: Pyramid
|
@@ -17,45 +17,48 @@ Classifier: License :: OSI Approved :: BSD License
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
18
18
|
Classifier: Programming Language :: Python
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
20
|
-
Classifier: Programming Language :: Python :: 3.8
|
21
20
|
Classifier: Programming Language :: Python :: 3.9
|
22
21
|
Classifier: Programming Language :: Python :: 3.10
|
23
22
|
Classifier: Programming Language :: Python :: 3.11
|
24
|
-
Classifier: Programming Language :: Python :: 3
|
25
|
-
Classifier: Programming Language :: Python :: 3.8
|
26
23
|
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
27
24
|
Classifier: Typing :: Typed
|
25
|
+
Provides-Extra: alembic
|
28
26
|
Provides-Extra: all
|
29
27
|
Provides-Extra: broadcast
|
28
|
+
Provides-Extra: debug
|
30
29
|
Provides-Extra: dev
|
31
30
|
Provides-Extra: oauth2
|
31
|
+
Provides-Extra: sentry
|
32
32
|
Provides-Extra: standard
|
33
33
|
Provides-Extra: test-images
|
34
|
-
|
35
|
-
|
36
|
-
Requires-Dist:
|
37
|
-
Requires-Dist:
|
38
|
-
Requires-Dist:
|
34
|
+
Provides-Extra: tests
|
35
|
+
Provides-Extra: webserver
|
36
|
+
Requires-Dist: SQLAlchemy (>=1.4.0,<3.0.0)
|
37
|
+
Requires-Dist: SQLAlchemy-Utils
|
38
|
+
Requires-Dist: alembic ; extra == "standard" or extra == "alembic" or extra == "all"
|
39
|
+
Requires-Dist: boltons ; extra == "tests" or extra == "all"
|
40
|
+
Requires-Dist: cee_syslog_handler
|
39
41
|
Requires-Dist: certifi
|
40
|
-
Requires-Dist: cornice
|
41
|
-
Requires-Dist: gunicorn ; extra == "standard" or extra == "all"
|
42
|
-
Requires-Dist: lxml ; extra == "
|
43
|
-
Requires-Dist:
|
44
|
-
Requires-Dist:
|
45
|
-
Requires-Dist: psycopg2
|
46
|
-
Requires-Dist: pyjwt ; extra == "oauth2" or extra == "all"
|
47
|
-
Requires-Dist: pyramid
|
48
|
-
Requires-Dist: pyramid-tm
|
42
|
+
Requires-Dist: cornice
|
43
|
+
Requires-Dist: gunicorn ; extra == "standard" or extra == "webserver" or extra == "all"
|
44
|
+
Requires-Dist: lxml ; extra == "tests" or extra == "all"
|
45
|
+
Requires-Dist: objgraph ; extra == "debug" or extra == "all"
|
46
|
+
Requires-Dist: prometheus-client
|
47
|
+
Requires-Dist: psycopg2
|
48
|
+
Requires-Dist: pyjwt ; extra == "standard" or extra == "oauth2" or extra == "all"
|
49
|
+
Requires-Dist: pyramid
|
50
|
+
Requires-Dist: pyramid-tm
|
51
|
+
Requires-Dist: pyramid_mako
|
49
52
|
Requires-Dist: pyyaml
|
50
|
-
Requires-Dist: redis ; extra == "standard" or extra == "broadcast" or extra == "all"
|
53
|
+
Requires-Dist: redis ; extra == "standard" or extra == "broadcast" or extra == "all"
|
51
54
|
Requires-Dist: requests
|
52
|
-
Requires-Dist: requests-oauthlib ; extra == "oauth2" or extra == "all"
|
55
|
+
Requires-Dist: requests-oauthlib ; extra == "standard" or extra == "oauth2" or extra == "all"
|
53
56
|
Requires-Dist: scikit-image ; extra == "test-images"
|
54
|
-
Requires-Dist: sentry-sdk ; extra == "standard" or extra == "all"
|
55
|
-
Requires-Dist: ujson
|
57
|
+
Requires-Dist: sentry-sdk ; extra == "standard" or extra == "sentry" or extra == "all"
|
58
|
+
Requires-Dist: ujson
|
56
59
|
Requires-Dist: waitress ; extra == "dev" or extra == "all"
|
57
|
-
Requires-Dist: zope.interface
|
58
|
-
Requires-Dist: zope.sqlalchemy
|
60
|
+
Requires-Dist: zope.interface
|
61
|
+
Requires-Dist: zope.sqlalchemy
|
59
62
|
Project-URL: Repository, https://github.com/camptocamp/c2cwsgiutils
|
60
63
|
Description-Content-Type: text/markdown
|
61
64
|
|
@@ -64,8 +67,7 @@ Description-Content-Type: text/markdown
|
|
64
67
|
This is a Python 3 library (>=3.5) providing common tools for Camptocamp WSGI
|
65
68
|
applications:
|
66
69
|
|
67
|
-
- Provide
|
68
|
-
a web application (statsd protocol)
|
70
|
+
- Provide prometheus metrics
|
69
71
|
- Allow to use a master/slave PostgresQL configuration
|
70
72
|
- Logging handler for CEE/UDP logs
|
71
73
|
- An optional view to change runtime the log levels
|
@@ -104,7 +106,7 @@ You should install `c2cwsgiutils` with the tool you use to manage your pip depen
|
|
104
106
|
|
105
107
|
In the `Dockerfile` you should add the following lines:
|
106
108
|
|
107
|
-
```
|
109
|
+
```dockerfile
|
108
110
|
# Generate the version file.
|
109
111
|
RUN c2cwsgiutils-genversion $(git rev-parse HEAD)
|
110
112
|
|
@@ -112,20 +114,19 @@ CMD ["gunicorn", "--paste=/app/production.ini"]
|
|
112
114
|
|
113
115
|
# Default values for the environment variables
|
114
116
|
ENV \
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
LOG_LEVEL=INFO
|
117
|
+
DEVELOPMENT=0 \
|
118
|
+
SQLALCHEMY_POOL_RECYCLE=30 \
|
119
|
+
SQLALCHEMY_POOL_SIZE=5 \
|
120
|
+
SQLALCHEMY_MAX_OVERFLOW=25 \
|
121
|
+
SQLALCHEMY_SLAVE_POOL_RECYCLE=30 \
|
122
|
+
SQLALCHEMY_SLAVE_POOL_SIZE=5 \
|
123
|
+
SQLALCHEMY_SLAVE_MAX_OVERFLOW=25\
|
124
|
+
LOG_TYPE=console \
|
125
|
+
OTHER_LOG_LEVEL=WARNING \
|
126
|
+
GUNICORN_LOG_LEVEL=WARNING \
|
127
|
+
SQL_LOG_LEVEL=WARNING \
|
128
|
+
C2CWSGIUTILS_LOG_LEVEL=WARNING \
|
129
|
+
LOG_LEVEL=INFO
|
129
130
|
```
|
130
131
|
|
131
132
|
Add in your `main` function.
|
@@ -161,8 +162,6 @@ The related environment variables:
|
|
161
162
|
- `SQL_LOG_LEVEL`: The SQL query log level, `WARNING`: no logs, `INFO`: logs the queries,
|
162
163
|
`DEBUG` also logs the results, default is `WARNING`.
|
163
164
|
- `GUNICORN_ERROR_LOG_LEVEL`: The Gunicorn error log level, default is `WARNING`.
|
164
|
-
- `GUNICORN_ACCESS_LOG_LEVEL`: The Gunicorn access log level, the logs have the level `INFO`,
|
165
|
-
default is `WARNING`.
|
166
165
|
- `C2CWSGIUTILS_CONFIG`: The fallback ini file to use by gunicorn, default is `production.ini`.
|
167
166
|
- `C2CWSGIUTILS_LOG_LEVEL`: The c2c WSGI utils log level, default is `WARNING`.
|
168
167
|
- `OTHER_LOG_LEVEL`: The log level for all the other logger, default is `WARNING`.
|
@@ -331,47 +330,6 @@ The requests module is also patched to monitor requests done without timeout. In
|
|
331
330
|
configure a default timeout with the `C2C_REQUESTS_DEFAULT_TIMEOUT` environment variable
|
332
331
|
(`c2c.requests_default_timeout`). If no timeout and no default is specified, a warning is issued.
|
333
332
|
|
334
|
-
## Metrics
|
335
|
-
|
336
|
-
To enable and configure the metrics framework, you can use:
|
337
|
-
|
338
|
-
- STATS_VIEW (c2c.stats_view): if defined, will enable the stats view `{C2C_BASE_PATH}/stats.json`
|
339
|
-
- STATSD_ADDRESS (c2c.statsd_address): if defined, send stats to the given statsd server
|
340
|
-
- STATSD_PREFIX (c2c.statsd_prefix): prefix to add to every metric names
|
341
|
-
- STATSD_USE_TAGS: If true, automatic metrics will use tags
|
342
|
-
- STATSD*TAG*{tag_name}: To set a global tag for the service
|
343
|
-
|
344
|
-
If enabled, some metrics are automatically generated:
|
345
|
-
|
346
|
-
- {STATSD_PREFIX}.route.{verb}.{route_name}.{status}: The time to process a query (includes rendering)
|
347
|
-
- {STATSD_PREFIX}.render.{verb}.{route_name}.{status}: The time to render a query
|
348
|
-
- {STATSD_PREFIX}.sql.{query}: The time to execute the given SQL query (simplified and normalized)
|
349
|
-
- {STATSD_PREFIX}.requests.{scheme}.{hostname}.{port}.{verb}.{status}: The time to execute HTTP requests to
|
350
|
-
outside services (only the time between the start of sending of the request and when the header is
|
351
|
-
back with a chunk of the body)
|
352
|
-
- {STATSD_PREFIX}.redis.{command}: The time to execute the given Redis command
|
353
|
-
|
354
|
-
You can manually measure the time spent on something like that:
|
355
|
-
|
356
|
-
```python
|
357
|
-
from c2cwsgiutils import stats
|
358
|
-
with stats.timer_context(['toto', 'tutu']):
|
359
|
-
do_something()
|
360
|
-
```
|
361
|
-
|
362
|
-
It will only add a timer event in case of success. If you want to measure both success and failures, do that:
|
363
|
-
|
364
|
-
```python
|
365
|
-
from c2cwsgiutils import stats
|
366
|
-
with stats.outcome_timer_context(['toto', 'tutu']):
|
367
|
-
do_something()
|
368
|
-
```
|
369
|
-
|
370
|
-
Other functions exists to generate metrics. Look at the `c2cwsgiutils.stats` module.
|
371
|
-
|
372
|
-
Look at the `c2cwsgiutils-stats-db` utility if you want to generate statistics (gauges) about the
|
373
|
-
row counts.
|
374
|
-
|
375
333
|
## SQL profiler
|
376
334
|
|
377
335
|
The SQL profiler must be configured with the `C2C_SQL_PROFILER_ENABLED` environment variable. That enables a view
|
@@ -519,27 +477,100 @@ If the `/app/versions.json` exists, a view is added (`{C2C_BASE_PATH}/versions.j
|
|
519
477
|
version of a app. This file is generated by calling the `c2cwsgiutils-genversion [$GIT_TAG] $GIT_HASH`
|
520
478
|
command line. Usually done in the [Dockerfile](acceptance_tests/app/Dockerfile) of the WSGI application.
|
521
479
|
|
522
|
-
##
|
480
|
+
## Prometheus
|
523
481
|
|
524
|
-
|
525
|
-
By default we have the `smap` `pss`, but we can easily add the `rss`, `size` or your custom settings:
|
482
|
+
[Prometheus client](https://github.com/prometheus/client_python) is integrated in c2cwsgiutils.
|
526
483
|
|
527
|
-
|
484
|
+
It will work in multi process mode with the limitation listed in the
|
485
|
+
[`prometheus_client` documentation](https://github.com/prometheus/client_python#multiprocess-mode-eg-gunicorn).
|
528
486
|
|
487
|
+
To enable it you should provide the `C2CWSGIUTILS_PROMETHEUS_PORT` environment variable.
|
488
|
+
For security reason, this port should not be exposed.
|
489
|
+
|
490
|
+
We can customize it with the following environment variables:
|
491
|
+
|
492
|
+
- `C2C_PROMETHEUS_PREFIX`: to customize the prefix, default is `c2cwsggiutils-`.
|
493
|
+
- `C2C_PROMETHEUS_PACKAGES` the packages that will be present in the version information, default is `c2cwsgiutils,pyramid,gunicorn,sqlalchemy`.
|
494
|
+
- `C2C_PROMETHEUS_APPLICATION_PACKAGE` the packages that will be present in the version information as application.
|
495
|
+
|
496
|
+
And you should add in your `gunicorn.conf.py`:
|
497
|
+
|
498
|
+
```python
|
499
|
+
from prometheus_client import multiprocess
|
500
|
+
|
501
|
+
|
502
|
+
def on_starting(server):
|
503
|
+
from c2cwsgiutils import prometheus
|
504
|
+
|
505
|
+
del server
|
506
|
+
|
507
|
+
prometheus.start()
|
508
|
+
|
509
|
+
|
510
|
+
def post_fork(server, worker):
|
511
|
+
from c2cwsgiutils import prometheus
|
512
|
+
|
513
|
+
del server, worker
|
514
|
+
|
515
|
+
prometheus.cleanup()
|
516
|
+
|
517
|
+
|
518
|
+
def child_exit(server, worker):
|
519
|
+
del server
|
520
|
+
|
521
|
+
multiprocess.mark_process_dead(worker.pid)
|
529
522
|
```
|
530
|
-
from import c2cwsgiutils.metrics import add_provider, Provider, MemoryMapProvider
|
531
523
|
|
532
|
-
|
533
|
-
|
534
|
-
|
524
|
+
In your `Dockerfile` you should add:
|
525
|
+
|
526
|
+
```dockerfile
|
527
|
+
RUN mkdir -p /prometheus-metrics \
|
528
|
+
&& chmod a+rwx /prometheus-metrics
|
529
|
+
ENV PROMETHEUS_MULTIPROC_DIR=/prometheus-metrics
|
530
|
+
```
|
535
531
|
|
536
|
-
|
537
|
-
return [({'metadata_key': 'matadata_value'}, metrics_value)]
|
532
|
+
### Add custom metric collector
|
538
533
|
|
539
|
-
|
540
|
-
|
534
|
+
See [official documentation](https://github.com/prometheus/client_python#custom-collectors).
|
535
|
+
|
536
|
+
Related to the Unix process.
|
537
|
+
|
538
|
+
```python
|
539
|
+
from c2cwsgiutils import broadcast, prometheus
|
540
|
+
|
541
|
+
prometheus.MULTI_PROCESS_COLLECTOR_BROADCAST_CHANNELS.append("prometheus_collector_custom")
|
542
|
+
broadcast.subscribe("c2cwsgiutils_prometheus_collect_gc", _broadcast_collector_custom)
|
543
|
+
my_custom_collector_instance = MyCustomCollector()
|
544
|
+
|
545
|
+
|
546
|
+
def _broadcast_collector_custom() -> List[prometheus.SerializedGauge]:
|
547
|
+
"""Get the collected GC gauges."""
|
548
|
+
|
549
|
+
return prometheus.serialize_collected_data(my_custom_collector_instance)
|
550
|
+
```
|
551
|
+
|
552
|
+
Related to the host, use that in the `gunicorn.conf.py`:
|
553
|
+
|
554
|
+
```python
|
555
|
+
def on_starting(server):
|
556
|
+
from c2cwsgiutils import prometheus
|
557
|
+
|
558
|
+
del server
|
559
|
+
|
560
|
+
registry = CollectorRegistry()
|
561
|
+
registry.register(MyCollector())
|
562
|
+
prometheus.start(registry)
|
541
563
|
```
|
542
564
|
|
565
|
+
### Database metrics
|
566
|
+
|
567
|
+
Look at the `c2cwsgiutils-stats-db` utility if you want to generate statistics (gauges) about the
|
568
|
+
row counts.
|
569
|
+
|
570
|
+
### Usage of metrics
|
571
|
+
|
572
|
+
With c2cwsgiutils each instance (Pod) has its own metrics, so we need to aggregate them to have the metrics for the service you probably need to use `sum by (<fields>) (<metric>)` to get the metric (especially for counters).
|
573
|
+
|
543
574
|
## Custom scripts
|
544
575
|
|
545
576
|
To have the application initialized in a script you should use the
|
@@ -592,7 +623,7 @@ have dumps of a few things:
|
|
592
623
|
- memory usage: `{C2C_BASE_PATH}/debug/memory?secret={C2C_SECRET}&limit=30&analyze_type=builtins.dict&python_internals_map=false`
|
593
624
|
- object ref: `{C2C_BASE_PATH}/debug/show_refs.dot?secret={C2C_SECRET}&analyze_type=gunicorn.app.wsgiapp.WSGIApplication&analyze_id=12345&max_depth=3&too_many=10&filter=1024&no_extra_info&backrefs`
|
594
625
|
`analyze_type` and `analyze_id` should not ve used toogether, you can use it like:
|
595
|
-
```
|
626
|
+
```bash
|
596
627
|
curl "<URL>" > /tmp/show_refs.dot
|
597
628
|
dot -Lg -Tpng /tmp/show_refs.dot > /tmp/show_refs.png
|
598
629
|
```
|
@@ -653,7 +684,7 @@ client. In production mode, you can still get them by sending the secret defined
|
|
653
684
|
|
654
685
|
If you want to use pyramid_debugtoolbar, you need to disable exception handling and configure it like that:
|
655
686
|
|
656
|
-
```
|
687
|
+
```ini
|
657
688
|
pyramid.includes =
|
658
689
|
pyramid_debugtoolbar
|
659
690
|
debugtoolbar.enabled = true
|
@@ -727,3 +758,41 @@ To make a release:
|
|
727
758
|
- Add the new branch name in the `.github/workflows/rebuild.yaml` and
|
728
759
|
`.github/workflows/audit.yaml` files.
|
729
760
|
|
761
|
+
## Testing
|
762
|
+
|
763
|
+
### Screenshots
|
764
|
+
|
765
|
+
To test the screenshots, you need to install `node` with `npm`, to do that add the following lines in your `Dockerfile`:
|
766
|
+
|
767
|
+
```dockerfile
|
768
|
+
RUN --mount=type=cache,target=/var/lib/apt/lists \
|
769
|
+
--mount=type=cache,target=/var/cache,sharing=locked \
|
770
|
+
. /etc/os-release \
|
771
|
+
&& echo "deb https://deb.nodesource.com/node_18.x ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/nodesource.list \
|
772
|
+
&& curl --silent https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
|
773
|
+
&& apt-get update \
|
774
|
+
&& apt-get install --assume-yes --no-install-recommends 'nodejs=18.*'
|
775
|
+
```
|
776
|
+
|
777
|
+
To do the image test call `check_screenshot` e.g.:
|
778
|
+
|
779
|
+
```python
|
780
|
+
def test_screenshot(app_connection):
|
781
|
+
image.check_screenshot(
|
782
|
+
app_connection.base_url + "my-path",
|
783
|
+
width=800,
|
784
|
+
height=600,
|
785
|
+
result_folder="results",
|
786
|
+
expected_filename=os.path.join(os.path.dirname(__file__), "my-check.expected.png"),
|
787
|
+
)
|
788
|
+
```
|
789
|
+
|
790
|
+
## Contributing
|
791
|
+
|
792
|
+
Install the pre-commit hooks:
|
793
|
+
|
794
|
+
```bash
|
795
|
+
pip install pre-commit
|
796
|
+
pre-commit install --allow-missing-config
|
797
|
+
```
|
798
|
+
|
@@ -3,8 +3,7 @@
|
|
3
3
|
This is a Python 3 library (>=3.5) providing common tools for Camptocamp WSGI
|
4
4
|
applications:
|
5
5
|
|
6
|
-
- Provide
|
7
|
-
a web application (statsd protocol)
|
6
|
+
- Provide prometheus metrics
|
8
7
|
- Allow to use a master/slave PostgresQL configuration
|
9
8
|
- Logging handler for CEE/UDP logs
|
10
9
|
- An optional view to change runtime the log levels
|
@@ -43,7 +42,7 @@ You should install `c2cwsgiutils` with the tool you use to manage your pip depen
|
|
43
42
|
|
44
43
|
In the `Dockerfile` you should add the following lines:
|
45
44
|
|
46
|
-
```
|
45
|
+
```dockerfile
|
47
46
|
# Generate the version file.
|
48
47
|
RUN c2cwsgiutils-genversion $(git rev-parse HEAD)
|
49
48
|
|
@@ -51,20 +50,19 @@ CMD ["gunicorn", "--paste=/app/production.ini"]
|
|
51
50
|
|
52
51
|
# Default values for the environment variables
|
53
52
|
ENV \
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
LOG_LEVEL=INFO
|
53
|
+
DEVELOPMENT=0 \
|
54
|
+
SQLALCHEMY_POOL_RECYCLE=30 \
|
55
|
+
SQLALCHEMY_POOL_SIZE=5 \
|
56
|
+
SQLALCHEMY_MAX_OVERFLOW=25 \
|
57
|
+
SQLALCHEMY_SLAVE_POOL_RECYCLE=30 \
|
58
|
+
SQLALCHEMY_SLAVE_POOL_SIZE=5 \
|
59
|
+
SQLALCHEMY_SLAVE_MAX_OVERFLOW=25\
|
60
|
+
LOG_TYPE=console \
|
61
|
+
OTHER_LOG_LEVEL=WARNING \
|
62
|
+
GUNICORN_LOG_LEVEL=WARNING \
|
63
|
+
SQL_LOG_LEVEL=WARNING \
|
64
|
+
C2CWSGIUTILS_LOG_LEVEL=WARNING \
|
65
|
+
LOG_LEVEL=INFO
|
68
66
|
```
|
69
67
|
|
70
68
|
Add in your `main` function.
|
@@ -100,8 +98,6 @@ The related environment variables:
|
|
100
98
|
- `SQL_LOG_LEVEL`: The SQL query log level, `WARNING`: no logs, `INFO`: logs the queries,
|
101
99
|
`DEBUG` also logs the results, default is `WARNING`.
|
102
100
|
- `GUNICORN_ERROR_LOG_LEVEL`: The Gunicorn error log level, default is `WARNING`.
|
103
|
-
- `GUNICORN_ACCESS_LOG_LEVEL`: The Gunicorn access log level, the logs have the level `INFO`,
|
104
|
-
default is `WARNING`.
|
105
101
|
- `C2CWSGIUTILS_CONFIG`: The fallback ini file to use by gunicorn, default is `production.ini`.
|
106
102
|
- `C2CWSGIUTILS_LOG_LEVEL`: The c2c WSGI utils log level, default is `WARNING`.
|
107
103
|
- `OTHER_LOG_LEVEL`: The log level for all the other logger, default is `WARNING`.
|
@@ -270,47 +266,6 @@ The requests module is also patched to monitor requests done without timeout. In
|
|
270
266
|
configure a default timeout with the `C2C_REQUESTS_DEFAULT_TIMEOUT` environment variable
|
271
267
|
(`c2c.requests_default_timeout`). If no timeout and no default is specified, a warning is issued.
|
272
268
|
|
273
|
-
## Metrics
|
274
|
-
|
275
|
-
To enable and configure the metrics framework, you can use:
|
276
|
-
|
277
|
-
- STATS_VIEW (c2c.stats_view): if defined, will enable the stats view `{C2C_BASE_PATH}/stats.json`
|
278
|
-
- STATSD_ADDRESS (c2c.statsd_address): if defined, send stats to the given statsd server
|
279
|
-
- STATSD_PREFIX (c2c.statsd_prefix): prefix to add to every metric names
|
280
|
-
- STATSD_USE_TAGS: If true, automatic metrics will use tags
|
281
|
-
- STATSD*TAG*{tag_name}: To set a global tag for the service
|
282
|
-
|
283
|
-
If enabled, some metrics are automatically generated:
|
284
|
-
|
285
|
-
- {STATSD_PREFIX}.route.{verb}.{route_name}.{status}: The time to process a query (includes rendering)
|
286
|
-
- {STATSD_PREFIX}.render.{verb}.{route_name}.{status}: The time to render a query
|
287
|
-
- {STATSD_PREFIX}.sql.{query}: The time to execute the given SQL query (simplified and normalized)
|
288
|
-
- {STATSD_PREFIX}.requests.{scheme}.{hostname}.{port}.{verb}.{status}: The time to execute HTTP requests to
|
289
|
-
outside services (only the time between the start of sending of the request and when the header is
|
290
|
-
back with a chunk of the body)
|
291
|
-
- {STATSD_PREFIX}.redis.{command}: The time to execute the given Redis command
|
292
|
-
|
293
|
-
You can manually measure the time spent on something like that:
|
294
|
-
|
295
|
-
```python
|
296
|
-
from c2cwsgiutils import stats
|
297
|
-
with stats.timer_context(['toto', 'tutu']):
|
298
|
-
do_something()
|
299
|
-
```
|
300
|
-
|
301
|
-
It will only add a timer event in case of success. If you want to measure both success and failures, do that:
|
302
|
-
|
303
|
-
```python
|
304
|
-
from c2cwsgiutils import stats
|
305
|
-
with stats.outcome_timer_context(['toto', 'tutu']):
|
306
|
-
do_something()
|
307
|
-
```
|
308
|
-
|
309
|
-
Other functions exists to generate metrics. Look at the `c2cwsgiutils.stats` module.
|
310
|
-
|
311
|
-
Look at the `c2cwsgiutils-stats-db` utility if you want to generate statistics (gauges) about the
|
312
|
-
row counts.
|
313
|
-
|
314
269
|
## SQL profiler
|
315
270
|
|
316
271
|
The SQL profiler must be configured with the `C2C_SQL_PROFILER_ENABLED` environment variable. That enables a view
|
@@ -458,27 +413,100 @@ If the `/app/versions.json` exists, a view is added (`{C2C_BASE_PATH}/versions.j
|
|
458
413
|
version of a app. This file is generated by calling the `c2cwsgiutils-genversion [$GIT_TAG] $GIT_HASH`
|
459
414
|
command line. Usually done in the [Dockerfile](acceptance_tests/app/Dockerfile) of the WSGI application.
|
460
415
|
|
461
|
-
##
|
416
|
+
## Prometheus
|
417
|
+
|
418
|
+
[Prometheus client](https://github.com/prometheus/client_python) is integrated in c2cwsgiutils.
|
419
|
+
|
420
|
+
It will work in multi process mode with the limitation listed in the
|
421
|
+
[`prometheus_client` documentation](https://github.com/prometheus/client_python#multiprocess-mode-eg-gunicorn).
|
422
|
+
|
423
|
+
To enable it you should provide the `C2CWSGIUTILS_PROMETHEUS_PORT` environment variable.
|
424
|
+
For security reason, this port should not be exposed.
|
425
|
+
|
426
|
+
We can customize it with the following environment variables:
|
427
|
+
|
428
|
+
- `C2C_PROMETHEUS_PREFIX`: to customize the prefix, default is `c2cwsggiutils-`.
|
429
|
+
- `C2C_PROMETHEUS_PACKAGES` the packages that will be present in the version information, default is `c2cwsgiutils,pyramid,gunicorn,sqlalchemy`.
|
430
|
+
- `C2C_PROMETHEUS_APPLICATION_PACKAGE` the packages that will be present in the version information as application.
|
431
|
+
|
432
|
+
And you should add in your `gunicorn.conf.py`:
|
433
|
+
|
434
|
+
```python
|
435
|
+
from prometheus_client import multiprocess
|
436
|
+
|
437
|
+
|
438
|
+
def on_starting(server):
|
439
|
+
from c2cwsgiutils import prometheus
|
440
|
+
|
441
|
+
del server
|
442
|
+
|
443
|
+
prometheus.start()
|
444
|
+
|
445
|
+
|
446
|
+
def post_fork(server, worker):
|
447
|
+
from c2cwsgiutils import prometheus
|
448
|
+
|
449
|
+
del server, worker
|
450
|
+
|
451
|
+
prometheus.cleanup()
|
452
|
+
|
453
|
+
|
454
|
+
def child_exit(server, worker):
|
455
|
+
del server
|
462
456
|
|
463
|
-
|
464
|
-
|
457
|
+
multiprocess.mark_process_dead(worker.pid)
|
458
|
+
```
|
459
|
+
|
460
|
+
In your `Dockerfile` you should add:
|
461
|
+
|
462
|
+
```dockerfile
|
463
|
+
RUN mkdir -p /prometheus-metrics \
|
464
|
+
&& chmod a+rwx /prometheus-metrics
|
465
|
+
ENV PROMETHEUS_MULTIPROC_DIR=/prometheus-metrics
|
466
|
+
```
|
467
|
+
|
468
|
+
### Add custom metric collector
|
469
|
+
|
470
|
+
See [official documentation](https://github.com/prometheus/client_python#custom-collectors).
|
471
|
+
|
472
|
+
Related to the Unix process.
|
473
|
+
|
474
|
+
```python
|
475
|
+
from c2cwsgiutils import broadcast, prometheus
|
465
476
|
|
466
|
-
|
477
|
+
prometheus.MULTI_PROCESS_COLLECTOR_BROADCAST_CHANNELS.append("prometheus_collector_custom")
|
478
|
+
broadcast.subscribe("c2cwsgiutils_prometheus_collect_gc", _broadcast_collector_custom)
|
479
|
+
my_custom_collector_instance = MyCustomCollector()
|
467
480
|
|
481
|
+
|
482
|
+
def _broadcast_collector_custom() -> List[prometheus.SerializedGauge]:
|
483
|
+
"""Get the collected GC gauges."""
|
484
|
+
|
485
|
+
return prometheus.serialize_collected_data(my_custom_collector_instance)
|
468
486
|
```
|
469
|
-
from import c2cwsgiutils.metrics import add_provider, Provider, MemoryMapProvider
|
470
487
|
|
471
|
-
|
472
|
-
def __init__(self):
|
473
|
-
super().__init__("my_metrics", "My Metric")
|
488
|
+
Related to the host, use that in the `gunicorn.conf.py`:
|
474
489
|
|
475
|
-
|
476
|
-
|
490
|
+
```python
|
491
|
+
def on_starting(server):
|
492
|
+
from c2cwsgiutils import prometheus
|
477
493
|
|
478
|
-
|
479
|
-
|
494
|
+
del server
|
495
|
+
|
496
|
+
registry = CollectorRegistry()
|
497
|
+
registry.register(MyCollector())
|
498
|
+
prometheus.start(registry)
|
480
499
|
```
|
481
500
|
|
501
|
+
### Database metrics
|
502
|
+
|
503
|
+
Look at the `c2cwsgiutils-stats-db` utility if you want to generate statistics (gauges) about the
|
504
|
+
row counts.
|
505
|
+
|
506
|
+
### Usage of metrics
|
507
|
+
|
508
|
+
With c2cwsgiutils each instance (Pod) has its own metrics, so we need to aggregate them to have the metrics for the service you probably need to use `sum by (<fields>) (<metric>)` to get the metric (especially for counters).
|
509
|
+
|
482
510
|
## Custom scripts
|
483
511
|
|
484
512
|
To have the application initialized in a script you should use the
|
@@ -531,7 +559,7 @@ have dumps of a few things:
|
|
531
559
|
- memory usage: `{C2C_BASE_PATH}/debug/memory?secret={C2C_SECRET}&limit=30&analyze_type=builtins.dict&python_internals_map=false`
|
532
560
|
- object ref: `{C2C_BASE_PATH}/debug/show_refs.dot?secret={C2C_SECRET}&analyze_type=gunicorn.app.wsgiapp.WSGIApplication&analyze_id=12345&max_depth=3&too_many=10&filter=1024&no_extra_info&backrefs`
|
533
561
|
`analyze_type` and `analyze_id` should not ve used toogether, you can use it like:
|
534
|
-
```
|
562
|
+
```bash
|
535
563
|
curl "<URL>" > /tmp/show_refs.dot
|
536
564
|
dot -Lg -Tpng /tmp/show_refs.dot > /tmp/show_refs.png
|
537
565
|
```
|
@@ -592,7 +620,7 @@ client. In production mode, you can still get them by sending the secret defined
|
|
592
620
|
|
593
621
|
If you want to use pyramid_debugtoolbar, you need to disable exception handling and configure it like that:
|
594
622
|
|
595
|
-
```
|
623
|
+
```ini
|
596
624
|
pyramid.includes =
|
597
625
|
pyramid_debugtoolbar
|
598
626
|
debugtoolbar.enabled = true
|
@@ -665,3 +693,41 @@ To make a release:
|
|
665
693
|
- Tag the GIT commit.
|
666
694
|
- Add the new branch name in the `.github/workflows/rebuild.yaml` and
|
667
695
|
`.github/workflows/audit.yaml` files.
|
696
|
+
|
697
|
+
## Testing
|
698
|
+
|
699
|
+
### Screenshots
|
700
|
+
|
701
|
+
To test the screenshots, you need to install `node` with `npm`, to do that add the following lines in your `Dockerfile`:
|
702
|
+
|
703
|
+
```dockerfile
|
704
|
+
RUN --mount=type=cache,target=/var/lib/apt/lists \
|
705
|
+
--mount=type=cache,target=/var/cache,sharing=locked \
|
706
|
+
. /etc/os-release \
|
707
|
+
&& echo "deb https://deb.nodesource.com/node_18.x ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/nodesource.list \
|
708
|
+
&& curl --silent https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \
|
709
|
+
&& apt-get update \
|
710
|
+
&& apt-get install --assume-yes --no-install-recommends 'nodejs=18.*'
|
711
|
+
```
|
712
|
+
|
713
|
+
To do the image test call `check_screenshot` e.g.:
|
714
|
+
|
715
|
+
```python
|
716
|
+
def test_screenshot(app_connection):
|
717
|
+
image.check_screenshot(
|
718
|
+
app_connection.base_url + "my-path",
|
719
|
+
width=800,
|
720
|
+
height=600,
|
721
|
+
result_folder="results",
|
722
|
+
expected_filename=os.path.join(os.path.dirname(__file__), "my-check.expected.png"),
|
723
|
+
)
|
724
|
+
```
|
725
|
+
|
726
|
+
## Contributing
|
727
|
+
|
728
|
+
Install the pre-commit hooks:
|
729
|
+
|
730
|
+
```bash
|
731
|
+
pip install pre-commit
|
732
|
+
pre-commit install --allow-missing-config
|
733
|
+
```
|