-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to serve metrics via http #20
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,18 @@ | ||
#!/usr/bin/env python3 | ||
|
||
from prometheus_client import CollectorRegistry, Gauge, write_to_textfile | ||
from prometheus_client import Gauge, write_to_textfile, start_http_server | ||
from prometheus_client import GC_COLLECTOR, PLATFORM_COLLECTOR, PROCESS_COLLECTOR # noqa E501 | ||
from prometheus_client.core import REGISTRY | ||
from time import sleep | ||
from pkg_exporter.pkgmanager import apt | ||
from pkg_exporter import reboot | ||
import argparse | ||
import os | ||
|
||
|
||
def main(): | ||
registry = CollectorRegistry() | ||
|
||
def populate_registry(rootdir=None): | ||
# get updates from apt | ||
pkgmanager = apt.AptPkgManager() | ||
pkgmanager = apt.AptPkgManager(rootdir=rootdir) | ||
|
||
# initially, check which metrics and labels are available | ||
metrics = pkgmanager.getMetricDict() | ||
|
@@ -20,20 +22,27 @@ def main(): | |
|
||
# also add reboot metrics | ||
rebootmanager = reboot.RebootManager() | ||
reboot_gauge = Gauge( | ||
"pkg_reboot_required", "Node Requires an Reboot", [], registry=registry | ||
) | ||
reboot_gauge = REGISTRY._names_to_collectors.get( | ||
"pkg_reboot_required", None) | ||
if not reboot_gauge: | ||
reboot_gauge = Gauge( | ||
"pkg_reboot_required", | ||
"Node Requires an Reboot", | ||
[]) | ||
|
||
# add update statistics | ||
meta_metric = pkgmanager.getMetaMetricDict() | ||
for key, value in meta_metric.items(): | ||
meta_gauges[key] = Gauge(f"pkg_{key}", value["description"], registry=registry) | ||
meta_gauges[key] = REGISTRY._names_to_collectors.get( | ||
f"pkg_{key}", None) | ||
if not meta_gauges[key]: | ||
meta_gauges[key] = Gauge(f"pkg_{key}", value["description"]) | ||
|
||
# Create all the gauge metrics | ||
for key, value in metrics.items(): | ||
gauges[key] = Gauge( | ||
f"pkg_{key}", value["description"], labels, registry=registry | ||
) | ||
gauges[key] = REGISTRY._names_to_collectors.get(f"pkg_{key}", None) | ||
if not gauges[key]: | ||
gauges[key] = Gauge(f"pkg_{key}", value["description"], labels) | ||
|
||
# let the pkgmanager query its internal metrics | ||
pkgmanager.query() | ||
|
@@ -50,12 +59,91 @@ def main(): | |
rebootmanager.query() | ||
reboot_gauge.set(rebootmanager.getMetricValue()) | ||
|
||
exporter_file = os.getenv("PKG_EXPORTER_FILE", "/var/prometheus/pkg-exporter.prom") | ||
|
||
def write_registry_to_file(registry, exporter_file=None): | ||
if not exporter_file: | ||
exporter_file = os.getenv( | ||
"PKG_EXPORTER_FILE", "/var/prometheus/pkg-exporter.prom" | ||
) | ||
exporter_dir = os.path.dirname(exporter_file) | ||
os.makedirs(exporter_dir, exist_ok=True) | ||
|
||
write_to_textfile(exporter_file, registry) | ||
|
||
|
||
def serve(addr, port, timewait, rootdir): | ||
start_http_server(addr=addr, port=port) | ||
while True: | ||
sleep(timewait) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a more elegant way of solving this? I'm not a fan of a static sleep, especially since this does not include the time "populate_registry" takes. |
||
populate_registry(rootdir) | ||
|
||
|
||
def processArgs(): | ||
parser = argparse.ArgumentParser( | ||
description="Collect metrics from apt and export it as a service" | ||
) | ||
group = parser.add_mutually_exclusive_group() | ||
|
||
group.add_argument( | ||
"-f", | ||
"--exporter-file", | ||
type=str, | ||
default=os.getenv( | ||
"PKG_EXPORTER_FILE", | ||
"/var/prometheus/pkg-exporter.prom"), | ||
help="File to export, if used the content will not be served", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, the default is "output to a file" anyway? The comment indicates that by default the HTTP Server is run. |
||
) | ||
group.add_argument( | ||
"-d", | ||
"--daemon", | ||
action="store_true", | ||
help="Run as daemon and server metrics via http", | ||
) | ||
parser.add_argument( | ||
"-a", | ||
"--bind-addr", | ||
type=str, | ||
default=os.getenv("PKG_EXPORTER_ADDR", "0.0.0.0"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should bind to a dualstack address, probably |
||
help="Bind address", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The help should display the default bind address. |
||
) | ||
parser.add_argument( | ||
"-p", | ||
"--port", | ||
type=int, | ||
default=os.getenv("PKG_EXPORTER_PORT", 8089), | ||
help="Bind port", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The help should display the default port |
||
) | ||
parser.add_argument( | ||
"-r", | ||
"--rootdir", | ||
type=str, | ||
default=os.getenv("PKG_EXPORTER_ROOT_DIR", None), | ||
help="Custom root directory for dpkg", | ||
) | ||
parser.add_argument( | ||
"-t", | ||
"--time-wait", | ||
type=int, | ||
default=os.getenv("PKG_EXPORTER_TIME_WAIT", 300), | ||
help="time (in second) to wait between data updates", | ||
) | ||
return parser.parse_args() | ||
|
||
|
||
def main(): | ||
args = processArgs() | ||
|
||
REGISTRY.unregister(GC_COLLECTOR) | ||
REGISTRY.unregister(PLATFORM_COLLECTOR) | ||
REGISTRY.unregister(PROCESS_COLLECTOR) | ||
|
||
populate_registry(args.rootdir) | ||
|
||
if not args.daemon: | ||
write_registry_to_file(REGISTRY, args.exporter_file) | ||
else: | ||
serve(args.bind_addr, args.port, args.time_wait, args.rootdir) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is duplicate, exporter_file is already filled with a default in line 87