Test Failed
Pull Request — develop (#3283)
by
unknown
02:56
created

glances.exports.glances_prometheus.Export.export()   B

Complexity

Conditions 7

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 22
nop 4
dl 0
loc 37
rs 7.952
c 0
b 0
f 0
1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""Prometheus interface class."""
10
11
import sys
12
from numbers import Number
13
14
from prometheus_client import Gauge, start_http_server
15
16
from glances.exports.export import GlancesExport
17
from glances.globals import listkeys
18
from glances.logger import logger
19
20
21
class Export(GlancesExport):
22
    """This class manages the Prometheus export module."""
23
24
    METRIC_SEPARATOR = '_'
25
26
    def __init__(self, config=None, args=None):
27
        """Init the Prometheus export IF."""
28
        super().__init__(config=config, args=args)
29
30
        # Load the Prometheus configuration file section
31
        self.export_enable = self.load_conf('prometheus', mandatories=['host', 'port', 'labels'], options=['prefix'])
32
        if not self.export_enable:
33
            exit('Missing PROMETHEUS config')
34
35
        # Optionals configuration keys
36
        if self.prefix is None:
37
            self.prefix = 'glances'
38
39
        if self.labels is None:
40
            self.labels = 'src:glances'
41
42
        # Init the metric dict
43
        # Perhaps a better method is possible...
44
        self._metric_dict = {}
45
46
        # Init the Prometheus Exporter
47
        self.init()
48
49
        self.plugin_to_object_label = {
50
            "amps": "app",
51
            "diskio": "device",
52
            "fs": "mount_point",
53
            "gpu": "name",
54
            "network": "interface",
55
            "percpu": "core",
56
            "sensors": "object",
57
            "wifi": "interface",
58
        }
59
60
    def init(self):
61
        """Init the Prometheus Exporter"""
62
        try:
63
            start_http_server(port=int(self.port), addr=self.host)
64
        except Exception as e:
65
            logger.critical(f"Can not start Prometheus exporter on {self.host}:{self.port} ({e})")
66
            sys.exit(2)
67
        else:
68
            logger.info(f"Start Prometheus exporter on {self.host}:{self.port}")
69
70
    def export(self, name, columns, points):
71
        """Write the points to the Prometheus exporter using Gauge."""
72
        logger.debug(f"Export {name} stats to Prometheus exporter")
73
74
        # Remove non number stats and convert all to float (for Boolean)
75
        data = {k: float(v) for k, v in zip(columns, points) if isinstance(v, Number)}
76
77
        # Write metrics to the Prometheus exporter
78
        for metric, value in data.items():
79
            metric = str(metric)
80
            try:
81
                obj, stat = metric.split('.')
82
                metric_name = self.prefix + self.METRIC_SEPARATOR + str(name) + self.METRIC_SEPARATOR + stat
83
            except ValueError:
84
                obj = ''
85
                metric_name = self.prefix + self.METRIC_SEPARATOR + str(metric)
86
87
            # Prometheus is very sensible to the metric name
88
            # See: https://prometheus.io/docs/practices/naming/
89
            for c in ' .-/:[]':
90
                metric_name = metric_name.replace(c, self.METRIC_SEPARATOR)
91
92
            labels = self.labels
93
            if obj:
94
                labels += f",{self.plugin_to_object_label[name]}:{obj}"
95
96
            # Get the labels
97
            labels = self.parse_tags(labels)
98
            # Manage an internal dict between metric name and Gauge
99
            if metric_name not in self._metric_dict:
100
                self._metric_dict[metric_name] = Gauge(metric_name, "", labelnames=listkeys(labels))
101
            # Write the value
102
            if hasattr(self._metric_dict[metric_name], 'labels'):
103
                # Add the labels (see issue #1255)
104
                self._metric_dict[metric_name].labels(**labels).set(value)
105
            else:
106
                self._metric_dict[metric_name].set(value)
107