mirror of
https://github.com/NohamR/prometheus-qbittorrent-exporter.git
synced 2026-01-10 16:18:38 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
940b5c6242 | ||
|
|
f47ebe94d0 | ||
|
|
e1ed34147c | ||
|
|
bc8676ed30 | ||
|
|
fb12d50373 | ||
|
|
bb3695ece6 | ||
|
|
a9a3e5d1e6 | ||
|
|
6c885b5fa0 | ||
|
|
039f7a7ef4 | ||
|
|
8bdaa48c48 | ||
|
|
e8dd24f731 | ||
|
|
12dcad10d5 | ||
|
|
597307c230 | ||
|
|
a25005b6a0 | ||
|
|
cfe62f8115 | ||
|
|
b5d20e3fe6 | ||
|
|
0ff6a56f18 | ||
|
|
2d968bc8e8 |
26
.github/workflows/docker.yml
vendored
26
.github/workflows/docker.yml
vendored
@@ -13,6 +13,9 @@ jobs:
|
|||||||
- name: 'Checkout GitHub Action'
|
- name: 'Checkout GitHub Action'
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
@@ -33,8 +36,27 @@ jobs:
|
|||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
- name: Build and push docker
|
- name: Build and push docker to DockerHub
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: esanchezm/prometheus-qbittorrent-exporter:latest,esanchezm/prometheus-qbittorrent-exporter:${{ steps.extract_branch.outputs.tag }}
|
platforms: linux/amd64,linux/arm64,linux/386
|
||||||
|
tags: |
|
||||||
|
${{ secrets.REGISTRY_USERNAME }}/prometheus-qbittorrent-exporter:latest
|
||||||
|
${{ secrets.REGISTRY_USERNAME }}/prometheus-qbittorrent-exporter:${{ steps.extract_branch.outputs.tag }}
|
||||||
|
|
||||||
|
- name: Login to Github Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.GHCR_PERSONAL_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push docker to Github Container Registry
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/386
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{ secrets.REGISTRY_USERNAME }}/prometheus-qbittorrent-exporter:latest
|
||||||
|
ghcr.io/${{ secrets.REGISTRY_USERNAME }}/prometheus-qbittorrent-exporter:${{ steps.extract_branch.outputs.tag }}
|
||||||
|
|||||||
@@ -20,7 +20,11 @@ qbittorrent-exporter
|
|||||||
Another option is run it in a docker container.
|
Another option is run it in a docker container.
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -e QBITTORRENT_PORT=8080 -e QBITTORRENT_HOST=myserver.local -p 8000:8000 esanchezm/prometheus-qbittorrent-exporter
|
docker run \
|
||||||
|
-e QBITTORRENT_PORT=8080 \
|
||||||
|
-e QBITTORRENT_HOST=myserver.local \
|
||||||
|
-p 8000:8000 \
|
||||||
|
ghcr.io/esanchezm/prometheus-qbittorrent-exporter
|
||||||
```
|
```
|
||||||
Add this to your prometheus.yml
|
Add this to your prometheus.yml
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from pythonjsonlogger import jsonlogger
|
|||||||
|
|
||||||
# Enable dumps on stderr in case of segfault
|
# Enable dumps on stderr in case of segfault
|
||||||
faulthandler.enable()
|
faulthandler.enable()
|
||||||
logger = None
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
class QbittorrentMetricsCollector():
|
class QbittorrentMetricsCollector():
|
||||||
@@ -29,7 +29,6 @@ class QbittorrentMetricsCollector():
|
|||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.torrents = None
|
|
||||||
self.client = Client(
|
self.client = Client(
|
||||||
host=config["host"],
|
host=config["host"],
|
||||||
port=config["port"],
|
port=config["port"],
|
||||||
@@ -38,12 +37,6 @@ class QbittorrentMetricsCollector():
|
|||||||
)
|
)
|
||||||
|
|
||||||
def collect(self):
|
def collect(self):
|
||||||
try:
|
|
||||||
self.torrents = self.client.torrents.info()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Couldn't get server info: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
metrics = self.get_qbittorrent_metrics()
|
metrics = self.get_qbittorrent_metrics()
|
||||||
|
|
||||||
for metric in metrics:
|
for metric in metrics:
|
||||||
@@ -75,11 +68,8 @@ class QbittorrentMetricsCollector():
|
|||||||
try:
|
try:
|
||||||
response = self.client.transfer.info
|
response = self.client.transfer.info
|
||||||
version = self.client.app.version
|
version = self.client.app.version
|
||||||
self.torrents = self.client.torrents.info()
|
except Exception as e:
|
||||||
except APIConnectionError as e:
|
logger.error(f"Couldn't get server info: {e}")
|
||||||
logger.error(f"Couldn't get server info: {e.error_message}")
|
|
||||||
except Exception:
|
|
||||||
logger.error(f"Couldn't get server info")
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -120,17 +110,15 @@ class QbittorrentMetricsCollector():
|
|||||||
def get_qbittorrent_torrent_tags_metrics(self):
|
def get_qbittorrent_torrent_tags_metrics(self):
|
||||||
try:
|
try:
|
||||||
categories = self.client.torrent_categories.categories
|
categories = self.client.torrent_categories.categories
|
||||||
|
torrents = self.client.torrents.info()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Couldn't fetch categories: {e}")
|
logger.error(f"Couldn't fetch torrent info: {e}")
|
||||||
return []
|
|
||||||
|
|
||||||
if not self.torrents:
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
metrics = []
|
metrics = []
|
||||||
categories.Uncategorized = AttrDict({'name': 'Uncategorized', 'savePath': ''})
|
categories.Uncategorized = AttrDict({'name': 'Uncategorized', 'savePath': ''})
|
||||||
for category in categories:
|
for category in categories:
|
||||||
category_torrents = [t for t in self.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}"
|
||||||
@@ -152,50 +140,66 @@ class QbittorrentMetricsCollector():
|
|||||||
|
|
||||||
class SignalHandler():
|
class SignalHandler():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.shutdown = False
|
self.shutdownCount = 0
|
||||||
|
|
||||||
# Register signal handler
|
# Register signal handler
|
||||||
signal.signal(signal.SIGINT, self._on_signal_received)
|
signal.signal(signal.SIGINT, self._on_signal_received)
|
||||||
signal.signal(signal.SIGTERM, self._on_signal_received)
|
signal.signal(signal.SIGTERM, self._on_signal_received)
|
||||||
|
|
||||||
def is_shutting_down(self):
|
def is_shutting_down(self):
|
||||||
return self.shutdown
|
return self.shutdownCount > 0
|
||||||
|
|
||||||
def _on_signal_received(self, signal, frame):
|
def _on_signal_received(self, signal, frame):
|
||||||
|
if self.shutdownCount > 1:
|
||||||
|
logger.warn("Forcibly killing exporter")
|
||||||
|
sys.exit(1)
|
||||||
logger.info("Exporter is shutting down")
|
logger.info("Exporter is shutting down")
|
||||||
self.shutdown = True
|
self.shutdownCount += 1
|
||||||
|
|
||||||
|
def get_config_value(key, default=""):
|
||||||
|
input_path = os.environ.get("FILE__" + key, None)
|
||||||
|
if input_path is not None:
|
||||||
|
try:
|
||||||
|
with open(input_path, "r") as input_file:
|
||||||
|
return input_file.read().strip()
|
||||||
|
except IOError as e:
|
||||||
|
logger.error(f"Unable to read value for {key} from {input_path}: {str(e)}")
|
||||||
|
|
||||||
|
return os.environ.get(key, default)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
config = {
|
# Init logger so it can be used
|
||||||
"host": os.environ.get("QBITTORRENT_HOST", ""),
|
|
||||||
"port": os.environ.get("QBITTORRENT_PORT", ""),
|
|
||||||
"username": os.environ.get("QBITTORRENT_USER", ""),
|
|
||||||
"password": os.environ.get("QBITTORRENT_PASS", ""),
|
|
||||||
"exporter_port": int(os.environ.get("EXPORTER_PORT", "8000")),
|
|
||||||
"log_level": os.environ.get("EXPORTER_LOG_LEVEL", "INFO"),
|
|
||||||
"metrics_prefix": os.environ.get("METRICS_PREFIX", "qbittorrent"),
|
|
||||||
}
|
|
||||||
|
|
||||||
# Register signal handler
|
|
||||||
signal_handler = SignalHandler()
|
|
||||||
|
|
||||||
# Init logger
|
|
||||||
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 = logging.getLogger()
|
|
||||||
logger.addHandler(logHandler)
|
logger.addHandler(logHandler)
|
||||||
|
logger.setLevel("INFO") # default until config is loaded
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"host": get_config_value("QBITTORRENT_HOST", ""),
|
||||||
|
"port": get_config_value("QBITTORRENT_PORT", ""),
|
||||||
|
"username": get_config_value("QBITTORRENT_USER", ""),
|
||||||
|
"password": get_config_value("QBITTORRENT_PASS", ""),
|
||||||
|
"exporter_port": int(get_config_value("EXPORTER_PORT", "8000")),
|
||||||
|
"log_level": get_config_value("EXPORTER_LOG_LEVEL", "INFO"),
|
||||||
|
"metrics_prefix": get_config_value("METRICS_PREFIX", "qbittorrent"),
|
||||||
|
}
|
||||||
|
# set level once config has been loaded
|
||||||
logger.setLevel(config["log_level"])
|
logger.setLevel(config["log_level"])
|
||||||
|
|
||||||
|
# Register signal handler
|
||||||
|
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 post 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
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
|
|||||||
setup(
|
setup(
|
||||||
name='prometheus-qbittorrent-exporter',
|
name='prometheus-qbittorrent-exporter',
|
||||||
packages=['qbittorrent_exporter'],
|
packages=['qbittorrent_exporter'],
|
||||||
version='1.1.1',
|
version='1.2.0',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
description='Prometheus exporter for qbittorrent',
|
description='Prometheus exporter for qbittorrent',
|
||||||
@@ -17,7 +17,7 @@ setup(
|
|||||||
keywords=['prometheus', 'qbittorrent'],
|
keywords=['prometheus', 'qbittorrent'],
|
||||||
classifiers=[],
|
classifiers=[],
|
||||||
python_requires='>=3',
|
python_requires='>=3',
|
||||||
install_requires=['qbittorrent-api==2021.3.18', 'prometheus_client==0.8.0', 'python-json-logger==0.1.5'],
|
install_requires=['attrdict==2.0.1', 'qbittorrent-api==2022.1.27', 'prometheus_client==0.12.0 ', 'python-json-logger==2.0.2'],
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'qbittorrent-exporter=qbittorrent_exporter.exporter:main',
|
'qbittorrent-exporter=qbittorrent_exporter.exporter:main',
|
||||||
|
|||||||
Reference in New Issue
Block a user