From 7401d6f2040311b5fc6a58c408e696baf8d9e2fb Mon Sep 17 00:00:00 2001 From: Patrick Tombez <35060345+p-tombez@users.noreply.github.com> Date: Wed, 4 Aug 2021 11:16:46 +0200 Subject: [PATCH 1/2] [13.0][ADD] monitoring_prometheus (#239) [13.0][ADD] monitoring_prometheus --- monitoring_prometheus/README.rst | 17 ++++++++ monitoring_prometheus/__init__.py | 2 + monitoring_prometheus/__manifest__.py | 22 ++++++++++ monitoring_prometheus/controllers/__init__.py | 1 + .../controllers/prometheus_metrics.py | 11 +++++ monitoring_prometheus/models/__init__.py | 1 + monitoring_prometheus/models/ir_http.py | 40 +++++++++++++++++++ requirements.txt | 1 + 8 files changed, 95 insertions(+) create mode 100644 monitoring_prometheus/README.rst create mode 100644 monitoring_prometheus/__init__.py create mode 100644 monitoring_prometheus/__manifest__.py create mode 100644 monitoring_prometheus/controllers/__init__.py create mode 100644 monitoring_prometheus/controllers/prometheus_metrics.py create mode 100644 monitoring_prometheus/models/__init__.py create mode 100644 monitoring_prometheus/models/ir_http.py diff --git a/monitoring_prometheus/README.rst b/monitoring_prometheus/README.rst new file mode 100644 index 0000000..aa98ffe --- /dev/null +++ b/monitoring_prometheus/README.rst @@ -0,0 +1,17 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License + +============================== +Monitoring: Prometheus metrics +============================== + +Add an endpoint */metrics* to allow a Prometheus server to fetch application metrics. +Current available metrics are: + +* Request completion time with 3 differentiators: + * Filestore + * Assets + * Everything else +* Longpolling request count + +No additional configuration is needed, just ensure that the Prometheus server is allowed to communicate with Odoo diff --git a/monitoring_prometheus/__init__.py b/monitoring_prometheus/__init__.py new file mode 100644 index 0000000..91c5580 --- /dev/null +++ b/monitoring_prometheus/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/monitoring_prometheus/__manifest__.py b/monitoring_prometheus/__manifest__.py new file mode 100644 index 0000000..22594d4 --- /dev/null +++ b/monitoring_prometheus/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2016-2019 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + + +{ + "name": "Monitoring: Prometheus Metrics", + "version": "13.0.1.0.0", + "author": "Camptocamp,Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "category", + "depends": [ + "base", + "web", + "server_environment", + ], + "website": "http://www.camptocamp.com", + "data": [], + "external_dependencies": { + "python": ["prometheus_client"], + }, + "installable": True, +} diff --git a/monitoring_prometheus/controllers/__init__.py b/monitoring_prometheus/controllers/__init__.py new file mode 100644 index 0000000..13ff72f --- /dev/null +++ b/monitoring_prometheus/controllers/__init__.py @@ -0,0 +1 @@ +from . import prometheus_metrics diff --git a/monitoring_prometheus/controllers/prometheus_metrics.py b/monitoring_prometheus/controllers/prometheus_metrics.py new file mode 100644 index 0000000..411a2ac --- /dev/null +++ b/monitoring_prometheus/controllers/prometheus_metrics.py @@ -0,0 +1,11 @@ +# Copyright 2016-2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo.http import Controller, route +from prometheus_client import generate_latest + + +class PrometheusController(Controller): + @route('/metrics', auth='public') + def metrics(self): + return generate_latest() diff --git a/monitoring_prometheus/models/__init__.py b/monitoring_prometheus/models/__init__.py new file mode 100644 index 0000000..9a5eb71 --- /dev/null +++ b/monitoring_prometheus/models/__init__.py @@ -0,0 +1 @@ +from . import ir_http diff --git a/monitoring_prometheus/models/ir_http.py b/monitoring_prometheus/models/ir_http.py new file mode 100644 index 0000000..0c026d6 --- /dev/null +++ b/monitoring_prometheus/models/ir_http.py @@ -0,0 +1,40 @@ +# Copyright 2016-2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +from odoo import models +from odoo.http import request +from prometheus_client import Summary, Counter + + +REQUEST_TIME = Summary( + "request_latency_sec", "Request response time in sec", ["query_type"] +) +LONGPOLLING_COUNT = Counter("longpolling", "Longpolling request count") + + +class IrHttp(models.AbstractModel): + _inherit = "ir.http" + + @classmethod + def _dispatch(cls): + path_info = request.httprequest.environ.get("PATH_INFO") + + if path_info.startswith("/longpolling/"): + LONGPOLLING_COUNT.inc() + return super()._dispatch() + + if path_info.startswith("/metrics"): + return super()._dispatch() + + if path_info.startswith("/web/static"): + label = "assets" + elif path_info.startswith("/web/content"): + label = "filestore" + else: + label = "client" + + res = None + with REQUEST_TIME.labels(label).time(): + res = super()._dispatch() + + return res diff --git a/requirements.txt b/requirements.txt index 8d39c30..0525441 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ python-keystoneclient==3.22.0 keystoneauth1==3.14.0 # error with 5.x (ConstructorError: could not determine a constructor for the tag '!record') PyYAML==4.2b4 +prometheus_client==0.11.0 From 4223e926f8071ade787cfd7dda619a0e33562f33 Mon Sep 17 00:00:00 2001 From: Yannick Vaucher Date: Wed, 3 Nov 2021 10:03:34 +0100 Subject: [PATCH 2/2] Backport monitoring_prometheus from 13.0 --- monitoring_prometheus/__manifest__.py | 5 +++-- .../controllers/prometheus_metrics.py | 10 +++++++++- monitoring_prometheus/models/ir_http.py | 16 ++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/monitoring_prometheus/__manifest__.py b/monitoring_prometheus/__manifest__.py index 22594d4..9893778 100644 --- a/monitoring_prometheus/__manifest__.py +++ b/monitoring_prometheus/__manifest__.py @@ -1,10 +1,11 @@ -# Copyright 2016-2019 Camptocamp SA +# -*- coding: utf-8 -*- +# Copyright 2016-2021 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) { "name": "Monitoring: Prometheus Metrics", - "version": "13.0.1.0.0", + "version": "10.0.1.0.0", "author": "Camptocamp,Odoo Community Association (OCA)", "license": "AGPL-3", "category": "category", diff --git a/monitoring_prometheus/controllers/prometheus_metrics.py b/monitoring_prometheus/controllers/prometheus_metrics.py index 411a2ac..2aca0cc 100644 --- a/monitoring_prometheus/controllers/prometheus_metrics.py +++ b/monitoring_prometheus/controllers/prometheus_metrics.py @@ -1,8 +1,16 @@ +# -*- coding: utf-8 -*- # Copyright 2016-2021 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +import logging from odoo.http import Controller, route -from prometheus_client import generate_latest + +_logger = logging.getLogger(__name__) + +try: + from prometheus_client import generate_latest +except (ImportError, IOError) as err: + _logger.warning(err) class PrometheusController(Controller): diff --git a/monitoring_prometheus/models/ir_http.py b/monitoring_prometheus/models/ir_http.py index 0c026d6..f610ef1 100644 --- a/monitoring_prometheus/models/ir_http.py +++ b/monitoring_prometheus/models/ir_http.py @@ -1,9 +1,17 @@ +# -*- coding: utf-8 -*- # Copyright 2016-2021 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +import logging from odoo import models from odoo.http import request -from prometheus_client import Summary, Counter + +_logger = logging.getLogger(__name__) + +try: + from prometheus_client import Summary, Counter +except (ImportError, IOError) as err: + _logger.warning(err) REQUEST_TIME = Summary( @@ -21,10 +29,10 @@ class IrHttp(models.AbstractModel): if path_info.startswith("/longpolling/"): LONGPOLLING_COUNT.inc() - return super()._dispatch() + return super(IrHttp, cls)._dispatch() if path_info.startswith("/metrics"): - return super()._dispatch() + return super(IrHttp, cls)._dispatch() if path_info.startswith("/web/static"): label = "assets" @@ -35,6 +43,6 @@ class IrHttp(models.AbstractModel): res = None with REQUEST_TIME.labels(label).time(): - res = super()._dispatch() + res = super(IrHttp, cls)._dispatch() return res