Overview
Prometheusには様々なexporterがあるが、APIを作ったりして
独自のmetricsを生成したい場合は、client libraryを使うようになっている。
従ってpythonにもそれ用のlibraryがあり、それがこちら。
基本的にはGaugeを使って作れば良いのでだいたい以下のようなコードになる。
#!/usr/bin/env python3
# import os
import sys
import re
from logging import getLogger
from prometheus_client import generate_latest
from prometheus_client import Gauge
from prometheus_client import REGISTRY
logger = getLogger(__name__)
class MetricsGenetator(object):
def __init__(self):
self.g = Gauge('my_inprogress_requests', 'Description of gauge')
def run(self):
self.g.inc() # Increment by 1
self.g.dec(10) # Decrement by given value
self.g.set(4.2) # Set to a given value
metrics = generate_latest(self.registry)
return metrics
def main():
app = MetricsGenerator()
metrics = app.run()
print(metrics)
if __name__ == '__main__':
main()
これを都度、実行しているならば良いが、
例えばself.g.setに渡されている4.2という値が以下のような使い方をしていると困ったことが起きる。
- snmpget cpu_usage from のように対象が分かれるだけでコード自体は使い回されている場合
- 上記のようなコードがモジュール化されており常時プロセスとしては起動し続けている
- 値が取得できたときは返すが、取得できない時にダミー値を入れない
具体的にはどのようなことが起きるかというと
- Aサーバから CPU usageを得る - 50 とする
- Bサーバから CPU usageを得ようとして失敗する (None)
- この時に、Noneが返ってきた場合に self.g.set()自体をスキップする
- するとBサーバの CPU usage が 50 として返る
つまり前の値がそのまま流用されてしまうのである。
上記のような構造の場合は CPU usage = None 等を明示的に入れれば良いのだが
Aサーバ、BサーバのCPU数が流動的で、 for cpu in cpu_num: というような形で
ラベルを動的に生成をしている場合はダミー値を入れることができない。
どうするか
2019年9月に新しくcommitされた CollectorRegistryのtarget_infoを使う事で
Registryを分けることができるようになる。
こんな感じ。
#!/usr/bin/env python3
# import os
import sys
import re
import click
from logging import getLogger
from prometheus_client import generate_latest
from prometheus_client import Gauge
# from prometheus_client import REGISTRY
from prometheus_client.core import CollectorRegistry
logger = getLogger(__name__)
class MetricsGenetator(object):
def __init__(self, target):
registry = CollectorRegistry(target_info={"target": server_name})
self.g = Gauge(
'my_inprogress_requests',
'Description of gauge',
registry=registry)
def run(self):
self.g.inc() # Increment by 1
self.g.dec(10) # Decrement by given value
self.g.set(4.2) # Set to a given value
metrics = generate_latest(self.registry)
return metrics
@click.command()
@click.option('-h', '--hostname', required=True, type=str,
default="localhost", help="get metrics from hostname. (default: localhost)")
def cli():
app = MetricsGenerator(target)
metrics = app.run()
print(metrics)
def main():
cli()
if __name__ == '__main__':
main()