Switch to attridict, format with Black

This commit is contained in:
Joel Heaps 2023-09-28 22:07:25 -05:00
parent 1fc4383a95
commit 31f309c942
3 changed files with 52 additions and 38 deletions

15
pdm.lock generated
View File

@ -6,18 +6,15 @@ groups = ["default"]
cross_platform = true cross_platform = true
static_urls = false static_urls = false
lock_version = "4.3" lock_version = "4.3"
content_hash = "sha256:619785556320f35b6aca0bf164725dc3afa5a1a978782d66a3add0b9e1ad8738" content_hash = "sha256:637d322b802d007082b71cf7d27382f3b6b5618434faf6b346358abe81fc2f7f"
[[package]] [[package]]
name = "attrdict" name = "attridict"
version = "2.0.1" version = "0.0.8"
summary = "A dict with attribute-style access" summary = "A dict implementation with support for easy and clean access of its values through attributes"
dependencies = [
"six",
]
files = [ files = [
{file = "attrdict-2.0.1-py2.py3-none-any.whl", hash = "sha256:9432e3498c74ff7e1b20b3d93b45d766b71cbffa90923496f82c4ae38b92be34"}, {file = "attridict-0.0.8-py3-none-any.whl", hash = "sha256:8ee65af81f7762354e4514c443bbc04786a924c8e3e610c7883d2efbf323df6d"},
{file = "attrdict-2.0.1.tar.gz", hash = "sha256:35c90698b55c683946091177177a9e9c0713a0860f0e049febd72649ccd77b70"}, {file = "attridict-0.0.8.tar.gz", hash = "sha256:23a17671b9439d36e2bdb0a69c09f033abab0900a9df178e0f89aa1b2c42c5cd"},
] ]
[[package]] [[package]]

View File

@ -6,10 +6,10 @@ authors = [
{name = "Esteban Sanchez", email = "esteban.sanchez@gmail.com"}, {name = "Esteban Sanchez", email = "esteban.sanchez@gmail.com"},
] ]
dependencies = [ dependencies = [
"attrdict>=2.0.1",
"prometheus-client>=0.17.1", "prometheus-client>=0.17.1",
"python-json-logger>=2.0.7", "python-json-logger>=2.0.7",
"qbittorrent-api>=2023.9.53", "qbittorrent-api>=2023.9.53",
"attridict>=0.0.8",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
readme = "README.md" readme = "README.md"

View File

@ -3,9 +3,8 @@ import os
import sys import sys
import signal import signal
import faulthandler import faulthandler
from attrdict import AttrDict import attridict
from qbittorrentapi import Client, TorrentStates from qbittorrentapi import Client, TorrentStates
from qbittorrentapi.exceptions import APIConnectionError
from prometheus_client import start_http_server from prometheus_client import start_http_server
from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY
import logging import logging
@ -17,7 +16,7 @@ faulthandler.enable()
logger = logging.getLogger() logger = logging.getLogger()
class QbittorrentMetricsCollector(): class QbittorrentMetricsCollector:
TORRENT_STATUSES = [ TORRENT_STATUSES = [
"checking", "checking",
"complete", "complete",
@ -34,7 +33,7 @@ class QbittorrentMetricsCollector():
port=config["port"], port=config["port"],
username=config["username"], username=config["username"],
password=config["password"], password=config["password"],
VERIFY_WEBUI_CERTIFICATE=config["verify_webui_certificate"] VERIFY_WEBUI_CERTIFICATE=config["verify_webui_certificate"],
) )
def collect(self): def collect(self):
@ -98,13 +97,13 @@ class QbittorrentMetricsCollector():
"name": f"{self.config['metrics_prefix']}_dl_info_data", "name": f"{self.config['metrics_prefix']}_dl_info_data",
"value": response.get("dl_info_data", 0), "value": response.get("dl_info_data", 0),
"help": "Data downloaded this session (bytes)", "help": "Data downloaded this session (bytes)",
"type": "counter" "type": "counter",
}, },
{ {
"name": f"{self.config['metrics_prefix']}_up_info_data", "name": f"{self.config['metrics_prefix']}_up_info_data",
"value": response.get("up_info_data", 0), "value": response.get("up_info_data", 0),
"help": "Data uploaded this session (bytes)", "help": "Data uploaded this session (bytes)",
"type": "counter" "type": "counter",
}, },
] ]
@ -117,29 +116,43 @@ class QbittorrentMetricsCollector():
return [] return []
metrics = [] metrics = []
categories.Uncategorized = AttrDict({'name': 'Uncategorized', 'savePath': ''}) categories.Uncategorized = attridict({"name": "Uncategorized", "savePath": ""})
for category in categories: for category in categories:
category_torrents = [t for t in torrents if t['category'] == category or (category == "Uncategorized" and t['category'] == "")] category_torrents = [
t
for t in torrents
if t["category"] == category
or (category == "Uncategorized" and t["category"] == "")
]
for status in self.TORRENT_STATUSES: for status in self.TORRENT_STATUSES:
status_prop = f"is_{status}" status_prop = f"is_{status}"
status_torrents = [ status_torrents = [
t for t in category_torrents if getattr(TorrentStates, status_prop).fget(TorrentStates(t['state'])) t
for t in category_torrents
if getattr(TorrentStates, status_prop).fget(
TorrentStates(t["state"])
)
] ]
metrics.append({ metrics.append(
"name": f"{self.config['metrics_prefix']}_torrents_count", {
"value": len(status_torrents), "name": f"{self.config['metrics_prefix']}_torrents_count",
"labels": { "value": len(status_torrents),
"status": status, "labels": {
"category": category, "status": status,
}, "category": category,
"help": f"Number of torrents in status {status} under category {category}" },
}) "help": (
f"Number of torrents in status {status} under category"
f" {category}"
),
}
)
return metrics return metrics
class SignalHandler(): class SignalHandler:
def __init__(self): def __init__(self):
self.shutdownCount = 0 self.shutdownCount = 0
@ -157,6 +170,7 @@ class SignalHandler():
logger.info("Exporter is shutting down") logger.info("Exporter is shutting down")
self.shutdownCount += 1 self.shutdownCount += 1
def get_config_value(key, default=""): def get_config_value(key, default=""):
input_path = os.environ.get("FILE__" + key, None) input_path = os.environ.get("FILE__" + key, None)
if input_path is not None: if input_path is not None:
@ -173,12 +187,11 @@ def main():
# Init logger so it can be used # Init logger so it can be used
logHandler = logging.StreamHandler() logHandler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter( formatter = jsonlogger.JsonFormatter(
"%(asctime) %(levelname) %(message)", "%(asctime) %(levelname) %(message)", datefmt="%Y-%m-%d %H:%M:%S"
datefmt="%Y-%m-%d %H:%M:%S"
) )
logHandler.setFormatter(formatter) logHandler.setFormatter(formatter)
logger.addHandler(logHandler) logger.addHandler(logHandler)
logger.setLevel("INFO") # default until config is loaded logger.setLevel("INFO") # default until config is loaded
config = { config = {
"host": get_config_value("QBITTORRENT_HOST", ""), "host": get_config_value("QBITTORRENT_HOST", ""),
@ -188,7 +201,9 @@ def main():
"exporter_port": int(get_config_value("EXPORTER_PORT", "8000")), "exporter_port": int(get_config_value("EXPORTER_PORT", "8000")),
"log_level": get_config_value("EXPORTER_LOG_LEVEL", "INFO"), "log_level": get_config_value("EXPORTER_LOG_LEVEL", "INFO"),
"metrics_prefix": get_config_value("METRICS_PREFIX", "qbittorrent"), "metrics_prefix": get_config_value("METRICS_PREFIX", "qbittorrent"),
"verify_webui_certificate": get_config_value("VERIFY_WEBUI_CERTIFICATE", "True") == "True", "verify_webui_certificate": (
get_config_value("VERIFY_WEBUI_CERTIFICATE", "True") == "True"
),
} }
# set level once config has been loaded # set level once config has been loaded
logger.setLevel(config["log_level"]) logger.setLevel(config["log_level"])
@ -197,10 +212,14 @@ def main():
signal_handler = SignalHandler() signal_handler = SignalHandler()
if not config["host"]: if not config["host"]:
logger.error("No host specified, please set QBITTORRENT_HOST environment variable") logger.error(
"No host specified, please set QBITTORRENT_HOST environment variable"
)
sys.exit(1) sys.exit(1)
if not config["port"]: if not config["port"]:
logger.error("No port specified, please set QBITTORRENT_PORT environment variable") logger.error(
"No port specified, please set QBITTORRENT_PORT environment variable"
)
sys.exit(1) sys.exit(1)
# Register our custom collector # Register our custom collector
@ -209,9 +228,7 @@ def main():
# Start server # Start server
start_http_server(config["exporter_port"]) start_http_server(config["exporter_port"])
logger.info( logger.info(f"Exporter listening on port {config['exporter_port']}")
f"Exporter listening on port {config['exporter_port']}"
)
while not signal_handler.is_shutting_down(): while not signal_handler.is_shutting_down():
time.sleep(1) time.sleep(1)