Skip to content

Commit 98addf0

Browse files
authored
provide auto registration facility for collectors (#18)
To provide more than one simple collector we refactor the registration of collectors to do this automatically. From now all collectors that follows the convention for new collectors. We also add a real life collector for load average (1, 5, 15). Finally we prepare the new major release.
1 parent 854fd18 commit 98addf0

File tree

11 files changed

+109
-26
lines changed

11 files changed

+109
-26
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[bumpversion]
22
commit = True
33
tag = False
4-
current_version = 0.1.0
4+
current_version = 1.0.0
55

66
[bumpversion:file:setup.py]

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a Ch
1313

1414
### Breaks
1515

16+
## 1.0.0 - (2021-03-22)
17+
18+
### New
19+
20+
* now it is simplier to add new collectors. You have to simply follow the naming convention
21+
* add loadavg collector as a real life example
22+
23+
### Breaks
24+
25+
* change load and registration behavior for collectors
26+
1627
## 0.1.0 - (2021-03-04)
1728

1829
### Changes

docs/plugins/p3exporter.collector.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,22 @@ p3exporter.collector package
55
:members:
66
:undoc-members:
77
:show-inheritance:
8+
9+
Submodules
10+
----------
11+
12+
p3exporter.collector.loadavg module
13+
-----------------------------------
14+
15+
.. automodule:: p3exporter.collector.loadavg
16+
:members:
17+
:undoc-members:
18+
:show-inheritance:
19+
20+
p3exporter.collector.my module
21+
------------------------------
22+
23+
.. automodule:: p3exporter.collector.my
24+
:members:
25+
:undoc-members:
26+
:show-inheritance:

p3.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
exporter_name: "Python prammable Prometheus exporter"
2+
collectors:
3+
- loadavg
4+
- my
25
credentials:

p3exporter/__init__.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
import sys
99
import time
1010

11-
from prometheus_client.core import REGISTRY
12-
13-
from p3exporter.collector import MyCollector, CollectorConfig
11+
from p3exporter.collector import Collector, CollectorConfig
1412
from p3exporter.web import create_app
1513

1614

@@ -43,8 +41,7 @@ def main():
4341
cfg = yaml.load(config_file, Loader=yaml.SafeLoader)
4442
collector_config = CollectorConfig(**cfg)
4543

46-
collector = MyCollector(collector_config)
47-
REGISTRY.register(collector)
44+
Collector(collector_config)
4845

4946
app = create_app(collector_config)
5047

p3exporter/collector/__init__.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"""Entry point for collector sub module."""
2-
import random
3-
import time
2+
import inflection
3+
import logging
44

5-
from prometheus_client.core import GaugeMetricFamily
5+
from importlib import import_module
6+
from prometheus_client.core import REGISTRY
67

78

89
class CollectorConfig(object):
@@ -16,6 +17,7 @@ def __init__(self, **kwargs):
1617
:raises Exception: Raises an exception if credentials are not well configured.
1718
"""
1819
self.exporter_name = kwargs.pop('exporter_name', None)
20+
self.collectors = kwargs.pop('collectors', [])
1921
self.credentials = kwargs.pop('credentials', None)
2022

2123
# do some fancy checks on configuration parameters
@@ -27,23 +29,25 @@ def __init__(self, **kwargs):
2729
raise Exception('Credential is not fully configured.')
2830

2931

30-
class MyCollector(object):
31-
"""A sample collector.
32+
class Collector(object):
33+
"""Base class to load collectors.
3234
33-
It does not really do much. It only runs a method and return the time it runs as a gauge metric.
35+
All collectors have to be placed inside the directory `collector`. You have to follow the naming convention:
36+
37+
1. Place the collector code in a <name>.py file (e.g. `my.py`)
38+
2. Within the file <name>.py` a class <Name>Collector (e.g. `MyController`) needs to be defined.
39+
This is the main collector class which will be imported, instantiate and registered automatically.
3440
"""
3541

3642
def __init__(self, config: CollectorConfig):
37-
"""Instanciate a MyCollector object."""
38-
pass
39-
40-
def collect(self):
41-
"""Collect the metrics."""
42-
self.timer = time.perf_counter()
43-
_run_process()
44-
runtime = time.perf_counter() - self.timer
45-
yield GaugeMetricFamily('my_process_runtime', 'Time a process runs in seconds', value=runtime)
46-
47-
def _run_process():
48-
"""Sample function to ran a command for metrics."""
49-
time.sleep(random.random()) # nosec
43+
for c in config.collectors:
44+
try:
45+
collector_module = import_module("p3exporter.collector.{}".format(c), package=None)
46+
collector_class = getattr(collector_module, "{0}Collector".format(inflection.camelize(c)))
47+
collector = collector_class(config)
48+
REGISTRY.register(collector)
49+
logging.info("Collector '{0}' was loaded and registred sucessfully".format(c))
50+
except ModuleNotFoundError as e:
51+
logging.warning("Collector '{0}' not loaded: {1}".format(c, e.msg))
52+
except AttributeError as e:
53+
logging.warning("Collector '{0}' not loaded: {1}".format(c, e))

p3exporter/collector/loadavg.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import os
2+
3+
from p3exporter.collector import CollectorConfig
4+
from prometheus_client.core import GaugeMetricFamily
5+
6+
class LoadavgCollector(object):
7+
8+
def __init__(self, config: CollectorConfig):
9+
"""Instanciate a CpuCollector object."""
10+
pass
11+
12+
def collect(self):
13+
"""Collect load avg for 1, 5 and 15 minutes interval.
14+
15+
Returns three gauge metrics. One for each load.
16+
"""
17+
self.avg1, self.avg5, self.avg15 = os.getloadavg()
18+
yield GaugeMetricFamily('load_avg_1', "1m load average.", value=self.avg1)
19+
yield GaugeMetricFamily('load_avg_5', "5m load average.", value=self.avg5)
20+
yield GaugeMetricFamily('load_avg_15', "15m load average.", value=self.avg15)

p3exporter/collector/my.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import random
2+
import time
3+
4+
from p3exporter.collector import CollectorConfig
5+
from prometheus_client.core import GaugeMetricFamily
6+
7+
class MyCollector(object):
8+
"""A sample collector.
9+
10+
It does not really do much. It only runs a method and return the time it runs as a gauge metric.
11+
"""
12+
13+
def __init__(self, config: CollectorConfig):
14+
"""Instanciate a MyCollector object."""
15+
pass
16+
17+
def collect(self):
18+
"""Collect the metrics."""
19+
self.timer = time.perf_counter()
20+
_run_process()
21+
runtime = time.perf_counter() - self.timer
22+
yield GaugeMetricFamily('my_process_runtime', 'Time a process runs in seconds', value=runtime)
23+
24+
def _run_process():
25+
"""Sample function to ran a command for metrics."""
26+
time.sleep(random.random()) # nosec

requirements-dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ flake8
44
flake8-colors
55
flake8-docstrings
66
flask
7+
inflection
78
twine
89
setuptools
910
PyYAML

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
inflection
12
prometheus-client
23
PyYAML
34
flask

0 commit comments

Comments
 (0)