From f62c283bedd6c74cbaf091c5b94a1ca81883998e Mon Sep 17 00:00:00 2001 From: Benjamin Hamon Date: Sat, 14 Mar 2026 17:17:39 +0100 Subject: [PATCH] Fix metrics for gunicorn --- Deployment/application.json | 1 + Deployment/dockerfile | 6 +++++- Deployment/gunicorn_configuration.py | 6 ++++++ .../application_factory.py | 17 +++++++++++++---- .../wsgi_entry_point.py | 3 ++- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Deployment/application.json b/Deployment/application.json index 703d972..2f8dd71 100644 --- a/Deployment/application.json +++ b/Deployment/application.json @@ -1,3 +1,4 @@ { + "server": "gunicorn", "metrics_token": "metrics" } diff --git a/Deployment/dockerfile b/Deployment/dockerfile index 809f312..3cfbca1 100644 --- a/Deployment/dockerfile +++ b/Deployment/dockerfile @@ -1,4 +1,4 @@ -# cspell:words a2enmod gunicorn libapache2 venv virtualenv wsgi +# cspell:words gunicorn venv FROM debian:12 @@ -18,6 +18,10 @@ COPY pip.conf /srv/application/.venv/pip.conf # Install the application and gunicorn RUN /srv/application/.venv/bin/python -m pip install --upgrade /srv/application/website "gunicorn ~= 23.0.0" +# Configure directory for prometheus metrics +RUN mkdir --parents /srv/prometheus +ENV PROMETHEUS_MULTIPROC_DIR=/srv/prometheus + # Open network port diff --git a/Deployment/gunicorn_configuration.py b/Deployment/gunicorn_configuration.py index fdb330a..90f9e1d 100644 --- a/Deployment/gunicorn_configuration.py +++ b/Deployment/gunicorn_configuration.py @@ -2,6 +2,8 @@ import multiprocessing +from prometheus_flask_exporter.multiprocess import GunicornInternalPrometheusMetrics + from benjaminhamon_standard_extensions.logging import logging_helpers @@ -41,3 +43,7 @@ } } } + + +def child_exit(server, worker): # pylint: disable = unused-argument + GunicornInternalPrometheusMetrics.mark_process_dead_on_child_exit(worker.pid) diff --git a/Sources/website/benjaminhamon_developer_website/application_factory.py b/Sources/website/benjaminhamon_developer_website/application_factory.py index eeb7e45..aee278b 100644 --- a/Sources/website/benjaminhamon_developer_website/application_factory.py +++ b/Sources/website/benjaminhamon_developer_website/application_factory.py @@ -2,12 +2,13 @@ import functools import logging -from typing import Callable, List +from typing import Callable, List, Optional import flask import jinja2 -import prometheus_flask_exporter import werkzeug.exceptions +from prometheus_flask_exporter import PrometheusMetrics +from prometheus_flask_exporter.multiprocess import GunicornInternalPrometheusMetrics import benjaminhamon_developer_website from benjaminhamon_developer_website.application import Application @@ -18,13 +19,13 @@ request_logger = logging.getLogger("Request") -def create_application(metrics_token: str) -> Application: +def create_application(metrics_token: str, server: Optional[str] = None) -> Application: flask_application = flask.Flask("benjaminhamon_developer_website") flask_application.config.update( METRICS_TOKEN = metrics_token, ) - prometheus_metrics = prometheus_flask_exporter.PrometheusMetrics(None, metrics_decorator = metrics_authorization) + prometheus_metrics = create_metrics(server) application = Application(flask_application) main_controller = MainController() @@ -36,6 +37,14 @@ def create_application(metrics_token: str) -> Application: return application +def create_metrics(server: Optional[str] = None) -> PrometheusMetrics: + if server is None: + return PrometheusMetrics(None, metrics_decorator = metrics_authorization) + if server == "gunicorn": + return GunicornInternalPrometheusMetrics(None, metrics_decorator = metrics_authorization) + raise ValueError("Unsupported server: '%s'" % server) + + def configure(application: flask.Flask) -> None: application.config["METADATA"] = { "product": benjaminhamon_developer_website.__product__, diff --git a/Sources/website/benjaminhamon_developer_website/wsgi_entry_point.py b/Sources/website/benjaminhamon_developer_website/wsgi_entry_point.py index 3b81ffa..903be7a 100644 --- a/Sources/website/benjaminhamon_developer_website/wsgi_entry_point.py +++ b/Sources/website/benjaminhamon_developer_website/wsgi_entry_point.py @@ -15,4 +15,5 @@ configuration = json.load(configuration_file) application = application_factory.create_application( - metrics_token = configuration["metrics_token"]) + metrics_token = configuration["metrics_token"], + server = configuration["server"])