From 47add4311fb8df75d07c5baf2d26c83f28547474 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Tue, 15 Sep 2026 16:59:59 +0200 Subject: [PATCH] fixup! [ADD] monitoring_prometheus_queue_job --- monitoring_prometheus/README.rst | 15 ++++++++++----- monitoring_prometheus/__manifest__.py | 2 +- .../controllers/prometheus_metrics.py | 18 ++++++++---------- monitoring_prometheus/models/__init__.py | 1 + .../models/prometheus_gatherer.py | 19 +++++++++---------- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/monitoring_prometheus/README.rst b/monitoring_prometheus/README.rst index 6cb7208..813dae2 100644 --- a/monitoring_prometheus/README.rst +++ b/monitoring_prometheus/README.rst @@ -20,11 +20,16 @@ Gathering metrics from the database ----------------------------------- Metrics that require a database query are collected by the *Prometheus: gather metrics* -scheduled action, which runs every 15 minutes and stores its result as JSON in the -``monitoring_prometheus.metrics`` system parameter. The */metrics* endpoint reads that -parameter and publishes the entries as gauges. This indirection is required because the -cron and the HTTP workers run in separate processes and cannot share the Prometheus -registry. +scheduled action, which runs every 15 minutes and stores its result as +``prometheus.metric`` records (one per metric name/labels combination). The */metrics* +endpoint reads those records and publishes them as gauges. This indirection is required +because the cron and the HTTP workers run in separate processes and cannot share the +Prometheus registry. + +By default, only users in the *Administration / Settings* group (``base.group_system``) +can read ``prometheus.metric`` records through the ORM/UI. The */metrics* endpoint +remains public and reads the records with ``sudo()`` so Prometheus can keep scraping it +without authentication. To publish new metrics, extend ``_gather_metrics`` on the ``prometheus.gatherer`` abstract model and return the result of ``super()`` extended with your own entries:: diff --git a/monitoring_prometheus/__manifest__.py b/monitoring_prometheus/__manifest__.py index 65aea1d..0656e73 100644 --- a/monitoring_prometheus/__manifest__.py +++ b/monitoring_prometheus/__manifest__.py @@ -10,7 +10,7 @@ "category": "category", "depends": ["base", "web", "server_environment"], "website": "https://github.com/camptocamp/odoo-cloud-platform", - "data": ["data/ir_cron.xml"], + "data": ["security/ir.model.access.csv", "data/ir_cron.xml"], "external_dependencies": {"python": ["prometheus_client"]}, "installable": True, } diff --git a/monitoring_prometheus/controllers/prometheus_metrics.py b/monitoring_prometheus/controllers/prometheus_metrics.py index 31e063c..13112fa 100644 --- a/monitoring_prometheus/controllers/prometheus_metrics.py +++ b/monitoring_prometheus/controllers/prometheus_metrics.py @@ -8,8 +8,6 @@ from prometheus_client import Gauge, generate_latest from odoo.http import Controller, request, route -from ..models.prometheus_gatherer import METRICS_PARAM - _logger = logging.getLogger(__name__) # Gauges must be instantiated only once per process, registering the same @@ -27,23 +25,23 @@ def _get_gauge(name, documentation, label_names): class PrometheusController(Controller): def _publish_gathered_metrics(self): - param = request.env["ir.config_parameter"].sudo().get_param(METRICS_PARAM) - if not param: + records = request.env["prometheus.metric"].sudo().search([]) + if not records: return cleared = set() - for metric in json.loads(param): - name = metric["name"] - labels = metric.get("labels") or {} - gauge = _get_gauge(name, metric.get("documentation", ""), sorted(labels)) + for record in records: + name = record.name + labels = json.loads(record.labels or "{}") + gauge = _get_gauge(name, record.documentation or "", sorted(labels)) if name not in cleared: # drop the series that disappeared since the last collection gauge.clear() cleared.add(name) if labels: - gauge.labels(**labels).set(metric["value"]) + gauge.labels(**labels).set(record.value) else: - gauge.set(metric["value"]) + gauge.set(record.value) @route("/metrics", auth="public") def metrics(self): diff --git a/monitoring_prometheus/models/__init__.py b/monitoring_prometheus/models/__init__.py index 52ebd90..3dd0e8c 100644 --- a/monitoring_prometheus/models/__init__.py +++ b/monitoring_prometheus/models/__init__.py @@ -1,2 +1,3 @@ from . import ir_http +from . import prometheus_metric from . import prometheus_gatherer diff --git a/monitoring_prometheus/models/prometheus_gatherer.py b/monitoring_prometheus/models/prometheus_gatherer.py index b1b0d83..b5ed748 100644 --- a/monitoring_prometheus/models/prometheus_gatherer.py +++ b/monitoring_prometheus/models/prometheus_gatherer.py @@ -1,19 +1,15 @@ # Copyright 2016-2021 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) -import json - from odoo import api, models -METRICS_PARAM = "monitoring_prometheus.metrics" - class PrometheusGatherer(models.AbstractModel): """Collect application metrics to be exposed on the /metrics endpoint. - Metrics are gathered by a cron and stored as JSON in an - ``ir.config_parameter``, because crons and HTTP workers run in - separate processes and cannot share the Prometheus registry. + Metrics are gathered by a cron and stored in ``prometheus.metric`` + records, because crons and HTTP workers run in separate processes and + cannot share the Prometheus registry. """ _name = "prometheus.gatherer" @@ -42,7 +38,10 @@ class PrometheusGatherer(models.AbstractModel): @api.model def _cron_gather_metrics(self): - self.env["ir.config_parameter"].sudo().set_param( - METRICS_PARAM, json.dumps(self._gather_metrics()) - ) + Metric = self.env["prometheus.metric"].sudo() + touched = self.env["prometheus.metric"] + for metric in self._gather_metrics(): + touched |= Metric._update_metric(metric) + # drop the series that disappeared since the last collection + (Metric.search([]) - touched).unlink() return True