fixes and typos

removed last trace of qbittorrent naming
created readme
This commit is contained in:
friendly Friend 2023-03-15 02:23:48 -07:00
parent f9866ee2b7
commit 610ca5b6b4
5 changed files with 948 additions and 135 deletions

View file

@ -1,6 +1,6 @@
# Prometheus qBittorrent exporter # Prometheus Immich exporter
A prometheus exporter for qBitorrent. Get metrics from a server and offers them in a prometheus format. A prometheus exporter for Immich. Get metrics from a server and offers them in a prometheus format.
## How to use it ## How to use it
@ -14,47 +14,54 @@ pip3 install prometheus-qbittorrent-exporter
Then you can run it with Then you can run it with
``` ```
qbittorrent-exporter immich-exporter
``` ```
Another option is run it in a docker container. Another option is to run it in a docker container. Here is an example docker run command
``` ```
docker run -e QBITTORRENT_PORT=8080 -e QBITTORRENT_HOST=myserver.local -p 8000:8000 esanchezm/prometheus-qbittorrent-exporter docker run -e IMMICH_PORT=8010 -e IMMICH_HOST=192.168.178.1 -p 8000:8000 friendlyfriend/prometheus-immich-exporter
``` ```
Add this to your prometheus.yml Add this to your prometheus.yml
``` ```
- job_name: "qbittorrent_exporter" - job_name: "qbittorrent_exporter"
static_configs: static_configs:
- targets: ['yourqbittorrentexporter:port'] - targets: ['yourimmichexporter:port']
``` ```
The application reads configuration using environment variables: The application reads configuration using environment variables:
| Environment variable | Default | Description | | Environment variable | Default | Description |
| -------------------- | ------------- | ----------- | |----------------------|----------|----------------------------------------------------|
| `QBITTORRENT_HOST` | | qbittorrent server hostname | | `IMMICH_HOST` | | immich server hostname |
| `QBITTORRENT_PORT` | | qbittorrent server port | | `IMMICH_PORT` | | immich server port |
| `QBITTORRENT_USER` | `""` | qbittorrent username |
| `QBITTORRENT_PASS` | `""` | qbittorrent password |
| `EXPORTER_PORT` | `8000` | Exporter listening port | | `EXPORTER_PORT` | `8000` | Exporter listening port |
| `EXPORTER_LOG_LEVEL` | `INFO` | Log level. One of: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | | `EXPORTER_LOG_LEVEL` | `INFO` | Log level. One of: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
| `METRICS_PREFIX` | `qbittorrent` | Prefix to add to all the metrics | | `METRICS_PREFIX` | `immich` | Prefix to add to all the metrics |
## Metrics ## Metrics
These are the metrics this program exports, assuming the `METRICS_PREFIX` is `qbittorrent`: These are the metrics this program exports, assuming the `METRICS_PREFIX` is `immich`:
| Metric name | Type | Description | | `metric name` | `description` |
| --------------------------------------------------- | -------- | ---------------- | |------------------------------------------|---------------------------------------------------------------------------|
| `qbittorrent_up` | gauge | Whether if the qBittorrent server is answering requests from this exporter. A `version` label with the server version is added | | `immich_server_info_version_number` | `pings server and passes version number with the use of labels={version}` |
| `qbittorrent_connected` | gauge | Whether if the qBittorrent server is connected to the Bittorrent network. | | `immich_server_info_diskAvailable` | `available space on disk` |
| `qbittorrent_firewalled` | gauge | Whether if the qBittorrent server is connected to the Bittorrent network but is behind a firewall. | | `immich_server_info_totalDiskSize` | `total disk size` |
| `qbittorrent_dht_nodes` | gauge | Number of DHT nodes connected to | | `immich_server_info_diskUse` | `disk space used by your system` |
| `qbittorrent_dl_info_data` | counter | Data downloaded since the server started, in bytes | | `immich_server_info_diskUsagePercentage` | `same as above but in percentage` |
| `qbittorrent_up_info_data` | counter | Data uploaded since the server started, in bytes |
| `qbittorrent_torrents_count` | gauge | Number of torrents for each `category` and `status`. Example: `qbittorrent_torrents_count{category="movies",status="downloading"}`| | `metric name` | `description` |
|---------------------------------------|---------------------------------------------|
| `immich_server_stats_user_count` | `number of users signed up ` |
| `immich_server_stats_photos_by_users` | `array of users and their amount of photos` |
| `immich_server_stats_photos_growth` | `sum of photos of all users` |
| `immich_server_stats_videos_by_users` | `array of users and their amount of videos` |
| `immich_server_stats_videos_growth` | `sum of all videos of all users` |
| `immich_server_stats_usage_by_users` | `the disk space each user uses` |
| `immich_server_stats_usage_growth` | `sum of disk space taken up by all users` |
## Screenshot ## Screenshot

View file

@ -2,7 +2,20 @@
## Import ## Import
To import the dashboard into your grafana, download the [dashboard.json](https://raw.githubusercontent.com/esanchezm/prometheus-qbittorrent-exporter/master/grafana/dashboard.json) file and import it into your server. Select your prometheus instance and that should be all. To import the dashboard into your grafana, download the [dashboard.json](https://raw.githubusercontent.com/friendlyFriend/immich-qbittorrent-exporter/master/grafana/dashboard.json) file and import it into your server. Select your prometheus instance and that should be all.
The graphs can be customized in their relative time. Mind that it takes time to populate them if you set relative time to monthly or yearly
| Relative time | entry |
|--------------------------|----------|
| `the current day so far` | `now/d` |
| `the current week` | `now/w` |
| `current month` | `now/M` |
| `current year` | `now/y` |
## Screenshot ## Screenshot

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 258 KiB

View file

@ -4,7 +4,6 @@ import sys
import signal import signal
import faulthandler import faulthandler
import requests import requests
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
@ -16,7 +15,7 @@ faulthandler.enable()
logger = logging.getLogger() logger = logging.getLogger()
class QbittorrentMetricsCollector(): class ImmichMetricsCollector:
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
@ -43,13 +42,14 @@ class QbittorrentMetricsCollector():
metrics = [] metrics = []
metrics.extend(self.get_immich_server_version_number()) metrics.extend(self.get_immich_server_version_number())
metrics.extend(self.get_immich_server_info()) metrics.extend(self.get_immich_server_info())
metrics.extend(self.get_immich_users_stat()) metrics.extend(self.get_immich_users_stat)
metrics.extend(self.get_immich_users_stat_growth())
return metrics return metrics
def get_immich_users_stat(self): def get_immich_users_stat_growth(self):
global response_user_stats
try: try:
endpoint_user_stats = "/api/server-info/stats" endpoint_user_stats = "/api/server-info/stats"
response_user_stats = requests.request( response_user_stats = requests.request(
@ -61,25 +61,58 @@ class QbittorrentMetricsCollector():
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error(f"Couldn't get server version: {e}") logger.error(f"Couldn't get server version: {e}")
metrics = [] userData = response_user_stats.json()["usageByUser"]
# photos growth gauge
userCount = len(response_user_stats.json()["usageByUser"]) userCount = len(response_user_stats.json()["usageByUser"])
photos_growth_total = 0
videos_growth_total = 0
usage_growth_total = 0
#userCount for x in range(0, userCount):
metrics.append( photos_growth_total += userData[x]["photos"]
# total video growth
videos_growth_total += userData[x]["videos"]
# total disk growth
usage_growth_total += userData[x]["usage"]
return [
{ {
"name": f"{self.config['metrics_prefix']}_user_count", "name": f"{self.config['metrics_prefix']}_server_stats_user_count",
"value": userCount, "value": userCount,
"help": "number of users on the immich server" "help": "number of users on the immich server"
},
{
"name": f"{self.config['metrics_prefix']}_server_stats_photos_growth",
"value": photos_growth_total,
"help": "photos counter that is added or removed"
},
{
"name": f"{self.config['metrics_prefix']}_server_stats_videos_growth",
"value": videos_growth_total,
"help": "videos counter that is added or removed"
},
{
"name": f"{self.config['metrics_prefix']}_server_stats_usage_growth",
"value": usage_growth_total,
"help": "videos counter that is added or removed"
} }
)
#Photos ]
@property
def get_immich_users_stat(self):
metrics = []
# To get the user count an api-endpoint exists but this works too. As a result one less api call is being made
userCount = len(response_user_stats.json()["usageByUser"])
# json array of all users with stats
userData = response_user_stats.json()["usageByUser"] userData = response_user_stats.json()["usageByUser"]
for x in range(0, userCount): for x in range(0, userCount):
metrics.append( metrics.append(
{ {
"name": f"{self.config['metrics_prefix']}_photos_by_user", "name": f"{self.config['metrics_prefix']}_server_stats_photos_by_users",
"value": userData[x]['photos'], "value": userData[x]['photos'],
"labels": { "labels": {
"firstName": userData[x]["userFirstName"], "firstName": userData[x]["userFirstName"],
@ -94,7 +127,7 @@ class QbittorrentMetricsCollector():
for x in range(0, userCount): for x in range(0, userCount):
metrics.append( metrics.append(
{ {
"name": f"{self.config['metrics_prefix']}_videos_by_user", "name": f"{self.config['metrics_prefix']}_server_stats_videos_by_users",
"value": userData[x]['videos'], "value": userData[x]['videos'],
"labels": { "labels": {
"firstName": userData[x]["userFirstName"], "firstName": userData[x]["userFirstName"],
@ -108,7 +141,7 @@ class QbittorrentMetricsCollector():
for x in range(0, userCount): for x in range(0, userCount):
metrics.append( metrics.append(
{ {
"name": f"{self.config['metrics_prefix']}_usage_by_user", "name": f"{self.config['metrics_prefix']}_server_stats_usage_by_users",
"value": (userData[x]['usage']), "value": (userData[x]['usage']),
"labels": { "labels": {
"firstName": userData[x]["userFirstName"], "firstName": userData[x]["userFirstName"],
@ -133,30 +166,26 @@ class QbittorrentMetricsCollector():
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
logger.error(f"Couldn't get server version: {e}") logger.error(f"Couldn't get server version: {e}")
return [ return [
{ {
"name": f"{self.config['metrics_prefix']}_diskAvailable", "name": f"{self.config['metrics_prefix']}_server_info_diskAvailable",
"value": (response_server_info.json()["diskAvailableRaw"]), "value": (response_server_info.json()["diskAvailableRaw"]),
"help": "Available space on disk", "help": "Available space on disk",
}, },
{ {
"name": f"{self.config['metrics_prefix']}_totalDiskSize", "name": f"{self.config['metrics_prefix']}_server_info_totalDiskSize",
"value": (response_server_info.json()["diskSizeRaw"]), "value": (response_server_info.json()["diskSizeRaw"]),
"help": "tota disk size", "help": "tota disk size",
# "type": "counter" # "type": "counter"
}, },
{ {
"name": f"{self.config['metrics_prefix']}_diskUse", "name": f"{self.config['metrics_prefix']}_server_info_diskUse",
"value": (response_server_info.json()["diskUseRaw"]), "value": (response_server_info.json()["diskUseRaw"]),
"help": "disk space in use", "help": "disk space in use",
# "type": "counter" # "type": "counter"
}, },
{ {
"name": f"{self.config['metrics_prefix']}_diskUsagePercentage", "name": f"{self.config['metrics_prefix']}_server_info_diskUsagePercentage",
"value": (response_server_info.json()["diskUsagePercentage"]), "value": (response_server_info.json()["diskUsagePercentage"]),
"help": "disk usage in percent", "help": "disk usage in percent",
# "type": "counter" # "type": "counter"
@ -166,6 +195,7 @@ class QbittorrentMetricsCollector():
def get_immich_server_version_number(self): def get_immich_server_version_number(self):
server_version_endpoint = "/api/server-info/version" server_version_endpoint = "/api/server-info/version"
response_server_version = ""
try: try:
@ -182,10 +212,9 @@ class QbittorrentMetricsCollector():
str(response_server_version.json()["patch"]) str(response_server_version.json()["patch"])
) )
return [ return [
{ {
"name": f"{self.config['metrics_prefix']}_version_number", "name": f"{self.config['metrics_prefix']}_server_info_version_number",
"value": bool(server_version_number), "value": bool(server_version_number),
"help": "server version number", "help": "server version number",
"labels": {"version": server_version_number} "labels": {"version": server_version_number}
@ -194,14 +223,14 @@ class QbittorrentMetricsCollector():
] ]
def combine_url(self, api_endpoint): def combine_url(self, api_endpoint):
prefix_url = "http://"
base_url = self.config["immich_host"] base_url = self.config["immich_host"]
base_url_port = self.config["immich_port"] base_url_port = self.config["immich_port"]
combined_url = base_url + ":" + base_url_port + api_endpoint combined_url = prefix_url + base_url + ":" + base_url_port + api_endpoint
return combined_url return combined_url
class SignalHandler(): class SignalHandler():
def __init__(self): def __init__(self):
self.shutdownCount = 0 self.shutdownCount = 0
@ -261,13 +290,16 @@ def main():
if not config["immich_host"]: if not config["immich_host"]:
logger.error("No host specified, please set IMMICH_HOST environment variable") logger.error("No host specified, please set IMMICH_HOST environment variable")
sys.exit(1) sys.exit(1)
if not config["immich_port"]:
logger.error("No host specified, please set IMMICH_PORT environment variable")
sys.exit(1)
if not config["token"]: if not config["token"]:
logger.error("No token specified, please set IMMICH_API_TOKEN environment variable") logger.error("No token specified, please set IMMICH_API_TOKEN environment variable")
sys.exit(1) sys.exit(1)
# Register our custom collector # Register our custom collector
logger.info("Exporter is starting up") logger.info("Exporter is starting up")
REGISTRY.register(QbittorrentMetricsCollector(config)) REGISTRY.register(ImmichMetricsCollector(config))
# Start server # Start server
start_http_server(config["exporter_port"]) start_http_server(config["exporter_port"])