Test Failed
Pull Request — develop (#3283)
by
unknown
02:31
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
            "containers": "container",
52
            "diskio": "device",
53
            "fs": "mount_point",
54
            "gpu": "name",
55
            "network": "interface",
56
            "percpu": "core",
57
            "sensors": "object",
58
            "wifi": "interface",
59
        }
60
61
    def init(self):
62
        """Init the Prometheus Exporter"""
63
        try:
64
            start_http_server(port=int(self.port), addr=self.host)
65
        except Exception as e:
66
            logger.critical(f"Can not start Prometheus exporter on {self.host}:{self.port} ({e})")
67
            sys.exit(2)
68
        else:
69
            logger.info(f"Start Prometheus exporter on {self.host}:{self.port}")
70
71
    def export(self, name, columns, points):
72
        """Write the points to the Prometheus exporter using Gauge."""
73
        logger.debug(f"Export {name} stats to Prometheus exporter")
74
75
        # Remove non number stats and convert all to float (for Boolean)
76
        data = {str(k): float(v) for k, v in zip(columns, points) if isinstance(v, Number)}
77
78
        # Write metrics to the Prometheus exporter
79
        for metric, value in data.items():
80
            metric_name = self.prefix + self.METRIC_SEPARATOR + name + self.METRIC_SEPARATOR
81
            try:
82
                obj, stat = metric.split('.')
83
                metric_name += stat
84
            except ValueError:
85
                obj = ''
86
                metric_name += metric
87
88
            # Prometheus is very sensible to the metric name
89
            # See: https://prometheus.io/docs/practices/naming/
90
            for c in ' .-/:[]':
91
                metric_name = metric_name.replace(c, self.METRIC_SEPARATOR)
92
93
            labels = self.labels
94
            if obj:
95
                labels += f",{self.plugin_to_object_label[name]}:{obj}"
96
97
            # Get the labels
98
            labels = self.parse_tags(labels)
99
            # Manage an internal dict between metric name and Gauge
100
            if metric_name not in self._metric_dict:
101
                self._metric_dict[metric_name] = Gauge(metric_name, "", labelnames=listkeys(labels))
102
            # Write the value
103
            if hasattr(self._metric_dict[metric_name], 'labels'):
104
                # Add the labels (see issue #1255)
105
                self._metric_dict[metric_name].labels(**labels).set(value)
106
            else:
107
                self._metric_dict[metric_name].set(value)
108