From 39c1b9e13cd067d896894cac5dffaef5af6d56b9 Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:47:01 +0300 Subject: [PATCH 1/7] using object as label --- .../exports/glances_prometheus/__init__.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/glances/exports/glances_prometheus/__init__.py b/glances/exports/glances_prometheus/__init__.py index fd922eb8..30546461 100644 --- a/glances/exports/glances_prometheus/__init__.py +++ b/glances/exports/glances_prometheus/__init__.py @@ -46,6 +46,15 @@ class Export(GlancesExport): # Init the Prometheus Exporter self.init() + self.plugin_to_object_label = { + "amps": "app", + "diskio": "device", + "fs": "mount_point", + "gpu": "name", + "network": "interface", + "percpu": "core", + } + def init(self): """Init the Prometheus Exporter""" try: @@ -64,21 +73,32 @@ class Export(GlancesExport): data = {k: float(v) for k, v in zip(columns, points) if isinstance(v, Number)} # Write metrics to the Prometheus exporter - for k, v in data.items(): - # Prometheus metric name: prefix_ - metric_name = self.prefix + self.METRIC_SEPARATOR + str(name) + self.METRIC_SEPARATOR + str(k) + for metric, value in data.items(): + metric = str(metric) + try: + obj, stat = metric.split('.') + metric_name = self.prefix + self.METRIC_SEPARATOR + str(name) + self.METRIC_SEPARATOR + stat + except ValueError: + obj = '' + metric_name = self.prefix + self.METRIC_SEPARATOR + str(metric) + # Prometheus is very sensible to the metric name # See: https://prometheus.io/docs/practices/naming/ for c in ' .-/:[]': metric_name = metric_name.replace(c, self.METRIC_SEPARATOR) + + labels = self.labels + if obj: + labels += f",{self.plugin_to_object_label[name]}:{obj}" + # Get the labels - labels = self.parse_tags(self.labels) + labels = self.parse_tags(labels) # Manage an internal dict between metric name and Gauge if metric_name not in self._metric_dict: - self._metric_dict[metric_name] = Gauge(metric_name, k, labelnames=listkeys(labels)) + self._metric_dict[metric_name] = Gauge(metric_name, "", labelnames=listkeys(labels)) # Write the value if hasattr(self._metric_dict[metric_name], 'labels'): # Add the labels (see issue #1255) - self._metric_dict[metric_name].labels(**labels).set(v) + self._metric_dict[metric_name].labels(**labels).set(value) else: - self._metric_dict[metric_name].set(v) + self._metric_dict[metric_name].set(value) From 9834e204c45a529cf9736ae45c3ded7b10ad629e Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:50:37 +0300 Subject: [PATCH 2/7] fix nonetype is not iterable error --- glances/exports/export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/exports/export.py b/glances/exports/export.py index 5d1544a5..7801e16f 100644 --- a/glances/exports/export.py +++ b/glances/exports/export.py @@ -225,7 +225,7 @@ class GlancesExport: def is_excluded(self, field): """Return true if the field is excluded.""" - return hasattr(self, 'exclude_fields') and any(re.fullmatch(i, field, re.I) for i in self.exclude_fields) + return any(re.fullmatch(i, field, re.I) for i in (getattr(self, 'exclude_fields') or ())) def plugins_to_export(self, stats): """Return the list of plugins to export. From 2c1abd666e66c1681aa2010abbcc7a5ab8165364 Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:51:54 +0300 Subject: [PATCH 3/7] spliting labels on the first ':' only as a label value might contain ':' --- glances/exports/export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/glances/exports/export.py b/glances/exports/export.py index 7801e16f..bbd37306 100644 --- a/glances/exports/export.py +++ b/glances/exports/export.py @@ -152,7 +152,7 @@ class GlancesExport: d_tags = {} if tags: try: - d_tags = dict([x.split(":") for x in tags.split(",")]) + d_tags = dict(x.split(":", 1) for x in tags.split(",")) except ValueError: # one of the 'key:value' pairs was missing logger.info("Invalid tags passed: %s", tags) From 63f0aacb7c26f77eee739000815489dd90ad6ead Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:19:11 +0300 Subject: [PATCH 4/7] add missing metrics to labels mappings --- glances/exports/glances_prometheus/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glances/exports/glances_prometheus/__init__.py b/glances/exports/glances_prometheus/__init__.py index 30546461..f0fb5c96 100644 --- a/glances/exports/glances_prometheus/__init__.py +++ b/glances/exports/glances_prometheus/__init__.py @@ -53,6 +53,8 @@ class Export(GlancesExport): "gpu": "name", "network": "interface", "percpu": "core", + "sensors": "object", + "wifi": "interface", } def init(self): From c8e67d07c14cd1040f2e10a5fe0152539e891d71 Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Tue, 23 Sep 2025 18:00:29 +0300 Subject: [PATCH 5/7] add missing metrics to labels mappings --- glances/exports/glances_prometheus/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/glances/exports/glances_prometheus/__init__.py b/glances/exports/glances_prometheus/__init__.py index f0fb5c96..4ee007bc 100644 --- a/glances/exports/glances_prometheus/__init__.py +++ b/glances/exports/glances_prometheus/__init__.py @@ -48,6 +48,7 @@ class Export(GlancesExport): self.plugin_to_object_label = { "amps": "app", + "containers": "container", "diskio": "device", "fs": "mount_point", "gpu": "name", From afdd09d6482dc9ddbaedaf572299cb69eb607486 Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Tue, 23 Sep 2025 22:27:33 +0300 Subject: [PATCH 6/7] fix bug in construction of metric name --- glances/exports/glances_prometheus/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/glances/exports/glances_prometheus/__init__.py b/glances/exports/glances_prometheus/__init__.py index 4ee007bc..9123d980 100644 --- a/glances/exports/glances_prometheus/__init__.py +++ b/glances/exports/glances_prometheus/__init__.py @@ -73,17 +73,17 @@ class Export(GlancesExport): logger.debug(f"Export {name} stats to Prometheus exporter") # Remove non number stats and convert all to float (for Boolean) - data = {k: float(v) for k, v in zip(columns, points) if isinstance(v, Number)} + data = {str(k): float(v) for k, v in zip(columns, points) if isinstance(v, Number)} # Write metrics to the Prometheus exporter for metric, value in data.items(): - metric = str(metric) + metric_name = self.prefix + self.METRIC_SEPARATOR + name + self.METRIC_SEPARATOR try: obj, stat = metric.split('.') - metric_name = self.prefix + self.METRIC_SEPARATOR + str(name) + self.METRIC_SEPARATOR + stat + metric_name += stat except ValueError: obj = '' - metric_name = self.prefix + self.METRIC_SEPARATOR + str(metric) + metric_name += metric # Prometheus is very sensible to the metric name # See: https://prometheus.io/docs/practices/naming/ From f53dbe748e2ae13b5828fa85d39af07cd4b3f329 Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Sat, 27 Sep 2025 12:34:40 +0300 Subject: [PATCH 7/7] getting object key dynamically instead of maintaining an hardcoded mapping --- .../exports/glances_prometheus/__init__.py | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/glances/exports/glances_prometheus/__init__.py b/glances/exports/glances_prometheus/__init__.py index 9123d980..85e2e5dd 100644 --- a/glances/exports/glances_prometheus/__init__.py +++ b/glances/exports/glances_prometheus/__init__.py @@ -13,11 +13,11 @@ from numbers import Number from prometheus_client import Gauge, start_http_server +from glances.api import GlancesStats from glances.exports.export import GlancesExport from glances.globals import listkeys from glances.logger import logger - class Export(GlancesExport): """This class manages the Prometheus export module.""" @@ -43,21 +43,11 @@ class Export(GlancesExport): # Perhaps a better method is possible... self._metric_dict = {} + self._stats = GlancesStats() + # Init the Prometheus Exporter self.init() - self.plugin_to_object_label = { - "amps": "app", - "containers": "container", - "diskio": "device", - "fs": "mount_point", - "gpu": "name", - "network": "interface", - "percpu": "core", - "sensors": "object", - "wifi": "interface", - } - def init(self): """Init the Prometheus Exporter""" try: @@ -75,14 +65,17 @@ class Export(GlancesExport): # Remove non number stats and convert all to float (for Boolean) data = {str(k): float(v) for k, v in zip(columns, points) if isinstance(v, Number)} + key_name = self._stats.get_plugin(name).get_key() + # Write metrics to the Prometheus exporter for metric, value in data.items(): + labels = self.labels metric_name = self.prefix + self.METRIC_SEPARATOR + name + self.METRIC_SEPARATOR try: obj, stat = metric.split('.') metric_name += stat + labels += f",{key_name}:{obj}" except ValueError: - obj = '' metric_name += metric # Prometheus is very sensible to the metric name @@ -90,10 +83,6 @@ class Export(GlancesExport): for c in ' .-/:[]': metric_name = metric_name.replace(c, self.METRIC_SEPARATOR) - labels = self.labels - if obj: - labels += f",{self.plugin_to_object_label[name]}:{obj}" - # Get the labels labels = self.parse_tags(labels) # Manage an internal dict between metric name and Gauge