Completed
Push — master ( 7fc0f8...8b57d9 )
by Nicolas
37s
created

glances/exports/glances_prometheus.py (1 issue)

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of Glances.
4
#
5
# Copyright (C) 2017 Nicolargo <[email protected]>
6
#
7
# Glances is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU Lesser General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Glances is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU Lesser General Public License for more details.
16
#
17
# You should have received a copy of the GNU Lesser General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20
"""Prometheus interface class."""
21
22
import sys
23
from datetime import datetime
24
from numbers import Number
25
26
from glances.logger import logger
27
from glances.exports.glances_export import GlancesExport
28
from glances.compat import iteritems
29
30
from prometheus_client import start_http_server, Gauge
31
32
33
class Export(GlancesExport):
34
35
    """This class manages the Prometheus export module."""
36
37
    METRIC_SEPARATOR = '_'
38
39 View Code Duplication
    def __init__(self, config=None, args=None):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
40
        """Init the Prometheus export IF."""
41
        super(Export, self).__init__(config=config, args=args)
42
43
        # Optionals configuration keys
44
        self.prefix = 'glances'
45
46
        # Load the Prometheus configuration file section
47
        self.export_enable = self.load_conf('prometheus',
48
                                            mandatories=['host', 'port'],
49
                                            options=['prefix'])
50
        if not self.export_enable:
51
            sys.exit(2)
52
53
        # Init the metric dict
54
        # Perhaps a better method is possible...
55
        self._metric_dict = {}
56
57
        # Init the Prometheus Exporter
58
        self.init()
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("Can not start Prometheus exporter on {}:{} ({})".format(self.host, self.port, e))
66
            sys.exit(2)
67
        else:
68
            logger.info("Start Prometheus exporter on {}:{}".format(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("Export {} stats to Prometheus exporter".format(name))
73
74
        # Remove non number stats and convert all to float (for Boolean)
75
        data = {k: float(v) for (k, v) in iteritems(dict(zip(columns, points))) if isinstance(v, Number)}
76
77
        # Write metrics to the Prometheus exporter
78
        for k, v in iteritems(data):
79
            # Prometheus metric name: prefix_<glances stats name>
80
            metric_name = self.prefix + self.METRIC_SEPARATOR + name + self.METRIC_SEPARATOR + k
81
            # Prometheus is very sensible to the metric name
82
            # See: https://prometheus.io/docs/practices/naming/
83
            for c in ['.', '-', '/', ' ']:
84
                metric_name = metric_name.replace(c, self.METRIC_SEPARATOR)
85
            # Manage an internal dict between metric name and Gauge
86
            if metric_name not in self._metric_dict:
87
                self._metric_dict[metric_name] = Gauge(metric_name, k)
88
            # Write the value
89
            self._metric_dict[metric_name].set(v)
90