59 Commits
1.0.1 ... 1.3.0

Author SHA1 Message Date
Esteban Sánchez
940b5c6242 Merge pull request #20 from jsawatzky/master
Return metrics on connection error
2023-05-09 15:52:59 +02:00
Jacob Sawatzky
f47ebe94d0 Fix typo 2023-04-25 21:10:56 -04:00
Jacob Sawatzky
e1ed34147c Return matrics on connection error 2023-04-25 21:06:30 -04:00
Esteban Sánchez
bc8676ed30 Changed reference to use GHCR 2023-03-16 16:44:08 +01:00
Esteban Sanchez
fb12d50373 Merge pull request #14 from averyanalex/master
Update dependencies
2022-02-28 09:45:10 +01:00
Alex Averyanov
bb3695ece6 Update dependencies 2022-01-11 22:56:54 +03:00
Esteban Sánchez
a9a3e5d1e6 Push docker image to GHCR 2021-08-13 13:02:04 +02:00
Esteban Sánchez
6c885b5fa0 Version 1.2.0 2021-07-17 23:19:38 +02:00
Esteban Sanchez
039f7a7ef4 Merge pull request #9 from jhollowe/logging-and-signals
Fixed logging and exit errors
2021-07-17 23:18:00 +02:00
Esteban Sanchez
8bdaa48c48 Merge pull request #10 from jhollowe/file-configs
Allow files as config sources
2021-07-17 23:16:48 +02:00
Esteban Sanchez
e8dd24f731 Merge pull request #8 from jhollowe/master
Add multi-arch Docker build support
2021-07-17 23:15:00 +02:00
John Hollowell
12dcad10d5 Setup logging before config loading 2021-07-16 23:24:58 +00:00
John Hollowell
597307c230 Allow files as config source
Allow "FILE__"+config_name to point to a file containing the config value.
This format matches linuxserver.io containers and is comonly used in other containers as well.

e.g. FILE__QBITTORRENT_HOST contains "/run/secrets/q_host" and the contents of the "/run/secrets/q_host" is "1.2.3.4"
2021-07-16 23:24:58 +00:00
John Hollowell
a25005b6a0 Fixed logging and exit errors
* Create logger in module so it is always available (still configured in main())
* Allow signals sent multiple times to forcibly kill the exporter
2021-07-16 23:24:26 +00:00
John Hollowell
cfe62f8115 Remove hardcoded username in docker tags 2021-07-16 21:15:47 +00:00
John Hollowell
b5d20e3fe6 Add multiple architechture build support
Add auto builds for x86, x86_64, and arm64
2021-07-16 21:01:54 +00:00
Esteban Sánchez
0ff6a56f18 Version 1.1.2 2021-07-03 23:33:39 +02:00
Esteban Sánchez
2d968bc8e8 Added attrdict as requirement 2021-07-03 20:59:50 +02:00
Esteban Sánchez
8ab307a630 Version 1.1.1 2021-07-02 16:18:11 +02:00
Esteban Sánchez
de00864f11 Small fixes 2021-07-02 16:07:53 +02:00
Esteban Sanchez
03d332979b Merge pull request #4 from angristan/update-api-dep
Update qbittorrent-api to 2021.3.18
2021-07-02 16:03:54 +02:00
Esteban Sanchez
1f6e8aa380 Merge pull request #6 from ndragon798/patch-1
Added info for prometheus.yml
2021-07-02 16:03:39 +02:00
Nathan Easton
ef00d90e2f Added info for prometheus.yml
As requested by #5
2021-07-01 14:05:50 -04:00
Stanislas
7f783f0b0a Update qbittorrent-api to 2021.3.18
To include 72687ca614 
in order to fix https://github.com/esanchezm/prometheus-qbittorrent-exporter/issues/3
2021-03-14 10:45:08 +01:00
Esteban Sánchez
345888ef68 Fixed category variable 2020-10-26 18:23:09 +01:00
Esteban Sánchez
0e4c4a97ea Added screenshot 2020-10-26 18:15:22 +01:00
Esteban Sánchez
d144f412ca Added screenshot and instructions 2020-10-26 18:02:40 +01:00
Esteban Sanchez
9002a709b7 Added grafana dashboard 2020-10-26 17:51:39 +01:00
Esteban Sánchez
b91bff0f0c Fixed output reference 2020-10-24 11:36:24 +02:00
Esteban Sánchez
ed44d88c5b Debug 2020-10-24 11:33:14 +02:00
Esteban Sánchez
8f0a0381d3 Get tag when it's a version release 2020-10-24 11:30:51 +02:00
Esteban Sanchez
b3e5e69f1f Get branch name to push 2020-10-24 10:52:22 +02:00
Esteban Sánchez
ef12d46431 Create docker image on release 2020-10-24 10:40:20 +02:00
Esteban Sánchez
9a19f46b7e Release 1.1.0 2020-10-24 10:39:49 +02:00
Esteban Sánchez
a53c7ae08b Install from src and not pip package 2020-10-24 10:39:40 +02:00
Esteban Sanchez
17ebcb0834 Setup buildx 2020-10-24 10:30:21 +02:00
Esteban Sanchez
7016e3de2b Set tags from a list 2020-10-24 10:13:39 +02:00
Esteban Sanchez
af51fd5b67 Set labels 2020-10-23 22:07:01 +02:00
Esteban Sanchez
82151b1d82 Setting tag 2020-10-23 22:04:55 +02:00
Esteban Sanchez
b6e3131929 Push to docker hub 2020-10-23 22:04:10 +02:00
Esteban Sanchez
688fcc54cb Testing 2020-10-23 22:02:24 +02:00
Esteban Sanchez
bc79a0dd52 Create pythonpublish.yml 2020-10-23 21:56:46 +02:00
Esteban Sanchez
af732087e8 Fixed version 2020-10-23 21:51:55 +02:00
Esteban Sanchez
15130f1c95 Added action to push to dockerhub 2020-10-23 21:49:29 +02:00
Esteban Sanchez
53dc38f796 Merge pull request #1 from Gabisonfire/master
Added the ability to include uncategorized torrents.
2020-10-22 23:11:35 +02:00
Gab
3bfc3f0673 Removed unused config 2020-10-21 18:58:17 -04:00
Esteban Sánchez
eee65ec84e Allow setting a metrics prefix 2020-10-21 08:05:51 +02:00
Gab
b4aefff06d Changed readme 2020-10-20 11:57:33 -04:00
Gab
baf14d495c Removed now useless logging message 2020-10-20 11:46:50 -04:00
Gab
003297ecfd Removed uncategorized from config 2020-10-20 11:45:48 -04:00
Gab
2497249b8c Fixed indentation 2020-10-20 02:10:39 -04:00
Gab
9e9f4a9ce4 Added scraping for uncategorized torrents 2020-10-20 02:10:04 -04:00
Gab
40d9bb964e Added scraping for uncategorized torrents 2020-10-20 02:05:17 -04:00
Esteban Sánchez
0b3fb04728 Removed unneded code and return earlier 2020-10-01 10:14:56 +02:00
Esteban Sánchez
13a52c40f2 New version 2020-09-29 13:47:32 +02:00
Esteban Sánchez
109924d0da Fixed up metric value and documentation 2020-09-29 13:46:41 +02:00
Esteban Sánchez
b391e5f83a Fixed docker run line 2020-09-29 12:03:26 +02:00
Esteban Sánchez
b924dd2bbb Added Dockerfile 2020-09-29 11:54:26 +02:00
Esteban Sánchez
28f1611079 Fixed first version 2020-09-29 11:52:09 +02:00
9 changed files with 1124 additions and 56 deletions

62
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Docker
on:
push:
branches: [ master ]
release:
types: [ created ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Extract branch name
shell: bash
run: |
tag=${GITHUB_REF#refs/tags/}
tag=${tag#refs/heads/}
if echo $tag | grep -q -E "[0-9]+\.[0-9]+\.[0-9]+"; then
tag="v$tag"
fi
echo "##[set-output name=tag;]$tag"
id: extract_branch
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push docker to DockerHub
uses: docker/build-push-action@v2
with:
push: true
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 }}

26
.github/workflows/pythonpublish.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.6'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist
twine upload dist/*

19
Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
FROM alpine:3.11
# Installing required packages
RUN apk add --update --no-cache \
python3
# Install package
WORKDIR /code
COPY . .
RUN pip3 install .
ENV QBITTORRENT_HOST=""
ENV QBITTORRENT_PORT=""
ENV QBITTORRENT_USER=""
ENV QBITTORRENT_PASS=""
ENV EXPORTER_PORT="8000"
ENV EXPORTER_LOG_LEVEL="INFO"
ENTRYPOINT ["qbittorrent-exporter"]

View File

@@ -20,9 +20,18 @@ qbittorrent-exporter
Another option is run it in a docker container.
```
docker run 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
```
- job_name: "qbittorrent_exporter"
static_configs:
- targets: ['yourqbittorrentexporter:port']
```
The application reads configuration using environment variables:
| Environment variable | Default | Description |
@@ -33,22 +42,29 @@ The application reads configuration using environment variables:
| `QBITTORRENT_PASS` | `""` | qbittorrent password |
| `EXPORTER_PORT` | `8000` | Exporter listening port |
| `EXPORTER_LOG_LEVEL` | `INFO` | Log level. One of: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
| `METRICS_PREFIX` | `qbittorrent` | Prefix to add to all the metrics |
## Metrics
These are the metrics this program exports:
These are the metrics this program exports, assuming the `METRICS_PREFIX` is `qbittorrent`:
| Metric name | Type | Description |
| --------------------------------------------------- | -------- | ---------------- |
| `qbittorrent_up` | gauge | Whether if the qBittorrent server is answering requests from this exporter. A `version` label with the server version is added |
| `qbittorrent_connected` | gauge | Whether if the qBittorrent server is connected to the Bittorrent network. |
| `qbittorrent_firewalled` | gauge | Whether if the qBittorrent server is connected to the Bittorrent network but is behind a firewall. |
| `qbittorrent_dht_nodes` | gauge | Number of DHT nodes connected to |
| `qbittorrent_dl_info_data` | counter | Data downloaded since the server started, in bytes |
| `qbittorrent_up_info_data` | counter | Data uploaded since the server started, in bytes |
| `torrents_count` | gauge | Number of torrents for each `category` and `status`. Example: `torrents_count{category="movies",status="downloading"}`|
| `qbittorrent_connected` | gauge | Whether if the qBittorrent server is connected to the Bittorrent network. |
| `qbittorrent_firewalled` | gauge | Whether if the qBittorrent server is connected to the Bittorrent network but is behind a firewall. |
| `qbittorrent_dht_nodes` | gauge | Number of DHT nodes connected to |
| `qbittorrent_dl_info_data` | counter | Data downloaded since the server started, in bytes |
| `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"}`|
## Screenshot
![](./grafana/screenshot.png)
[More info](./grafana/README.md)
## License

9
grafana/README.md Normal file
View File

@@ -0,0 +1,9 @@
# Grafana dashboard
## 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.
## Screenshot
![](./screenshot.png)

927
grafana/dashboard.json Normal file
View File

@@ -0,0 +1,927 @@
{
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__requires": [
{
"type": "panel",
"id": "gauge",
"name": "Gauge",
"version": ""
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.2.2"
},
{
"type": "panel",
"id": "grafana-piechart-panel",
"name": "Pie Chart",
"version": "1.6.1"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
},
{
"type": "panel",
"id": "stat",
"name": "Stat",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"iteration": 1603732792335,
"links": [],
"panels": [
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [
{
"from": "",
"id": 0,
"text": "Online",
"to": "",
"type": 1,
"value": "1"
},
{
"from": "",
"id": 1,
"text": "Offline",
"to": "",
"type": 1,
"value": "0"
},
{
"from": "",
"id": 2,
"text": "Unknown",
"to": "",
"type": 1,
"value": "null"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "yellow",
"value": null
},
{
"color": "semi-dark-red",
"value": 0
},
{
"color": "semi-dark-green",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "qbittorrent_up",
"format": "time_series",
"instant": false,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Qbittorrent Status",
"transformations": [],
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [
{
"from": "",
"id": 0,
"text": "Connected",
"to": "",
"type": 1,
"value": "1"
},
{
"from": "",
"id": 1,
"text": "Disconnected",
"to": "",
"type": 1,
"value": "0"
},
{
"from": "",
"id": 2,
"text": "Firewalled",
"to": "",
"type": 1,
"value": "-1"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "yellow",
"value": null
},
{
"color": "dark-red",
"value": 0
},
{
"color": "semi-dark-green",
"value": 1
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 3,
"y": 0
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "qbittorrent_connected - qbittorrent_firewalled",
"format": "time_series",
"instant": true,
"interval": "",
"legendFormat": "Status",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Bittorrent network",
"transformations": [],
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [
{
"from": "",
"id": 1,
"text": "No DHT",
"to": "",
"type": 1,
"value": "0"
},
{
"from": "",
"id": 2,
"text": "Firewalled",
"to": "",
"type": 1,
"value": "-1"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "yellow",
"value": null
},
{
"color": "dark-red",
"value": 0
},
{
"color": "semi-dark-green",
"value": 100
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 6,
"y": 0
},
"id": 13,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "qbittorrent_dht_nodes",
"instant": true,
"interval": "",
"legendFormat": "DHT nodes",
"refId": "B"
}
],
"timeFrom": null,
"timeShift": null,
"title": "DHT nodes",
"transformations": [],
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "decbytes"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 9,
"y": 0
},
"id": 5,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "increase(qbittorrent_dl_info_data_total[100y])",
"instant": false,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Data downloaded",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "decbytes"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 12,
"y": 0
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "increase(qbittorrent_up_info_data_total[100y])",
"instant": false,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Data uploaded",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 15,
"y": 0
},
"id": 7,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "increase(qbittorrent_up_info_data_total[100y]) / increase(qbittorrent_dl_info_data_total[100y])",
"instant": false,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Accumulated ratio",
"type": "stat"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 1,
"mappings": [],
"max": 48000000,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "dark-red",
"value": null
},
{
"color": "dark-green",
"value": 8000000
}
]
},
"unit": "binBps"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 18,
"y": 0
},
"id": 16,
"options": {
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "rate(qbittorrent_dl_info_data_total[1m])",
"instant": false,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Download speed",
"type": "gauge"
},
{
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {},
"decimals": 1,
"mappings": [
{
"from": "",
"id": 1,
"text": "",
"to": "",
"type": 1
}
],
"max": 18000000,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "dark-red",
"value": null
},
{
"color": "dark-green",
"value": 4000000
}
]
},
"unit": "binBps"
},
"overrides": []
},
"gridPos": {
"h": 4,
"w": 3,
"x": 21,
"y": 0
},
"id": 17,
"options": {
"orientation": "auto",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true
},
"pluginVersion": "7.2.2",
"targets": [
{
"expr": "rate(qbittorrent_up_info_data_total[1m])",
"hide": false,
"instant": false,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Upload speed",
"type": "gauge"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 10,
"w": 24,
"x": 0,
"y": 4
},
"hiddenSeries": false,
"id": 15,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.2.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(qbittorrent_dl_info_data_total[1m])",
"interval": "",
"legendFormat": "Download",
"refId": "A"
},
{
"expr": "rate(qbittorrent_up_info_data_total[1m])",
"interval": "",
"legendFormat": "Upload",
"refId": "B"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Transfer ratio",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "binBps",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "binBps",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": true,
"cacheTimeout": null,
"dashLength": 10,
"dashes": false,
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 10,
"w": 14,
"x": 0,
"y": 14
},
"hiddenSeries": false,
"id": 11,
"interval": null,
"legend": {
"alignAsTable": true,
"avg": false,
"current": false,
"max": false,
"min": false,
"rightSide": true,
"show": true,
"total": false,
"values": false
},
"lines": false,
"linewidth": 0,
"links": [],
"nullPointMode": "null as zero",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "7.2.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": true,
"targets": [
{
"expr": "sum(qbittorrent_torrents_count{category=~\"${categories}\", status!=\"complete\"}) by (status)",
"interval": "",
"legendFormat": "{{status}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Torrents by status",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"breakPoint": "50%",
"cacheTimeout": null,
"combine": {
"label": "Others",
"threshold": 0
},
"datasource": "${DS_PROMETHEUS}",
"fieldConfig": {
"defaults": {
"custom": {
"align": null,
"filterable": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"fontSize": "80%",
"format": "short",
"gridPos": {
"h": 10,
"w": 10,
"x": 14,
"y": 14
},
"id": 12,
"interval": null,
"legend": {
"percentage": true,
"show": true,
"sideWidth": null,
"values": true
},
"legendType": "Right side",
"links": [],
"nullPointMode": "connected",
"pieType": "donut",
"pluginVersion": "7.2.0",
"strokeWidth": 1,
"targets": [
{
"expr": "sum(qbittorrent_torrents_count{category=~\"${categories}\",status!=\"complete\"}) by (category)",
"interval": "",
"legendFormat": "{{category}}",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Torrents by categories",
"type": "grafana-piechart-panel",
"valueName": "current"
}
],
"refresh": "30s",
"schemaVersion": 26,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"allValue": ".*",
"current": {},
"datasource": "${DS_PROMETHEUS}",
"definition": "label_values(qbittorrent_torrents_count, category)",
"hide": 0,
"includeAll": true,
"label": "Categories",
"multi": true,
"name": "categories",
"options": [],
"query": "label_values(qbittorrent_torrents_count, category)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-5m",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Qbittorrent",
"uid": "eKyTETFMk",
"version": 40
}

BIN
grafana/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

View File

@@ -3,7 +3,9 @@ import os
import sys
import signal
import faulthandler
from attrdict import AttrDict
from qbittorrentapi import Client, TorrentStates
from qbittorrentapi.exceptions import APIConnectionError
from prometheus_client import start_http_server
from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY
import logging
@@ -12,7 +14,7 @@ from pythonjsonlogger import jsonlogger
# Enable dumps on stderr in case of segfault
faulthandler.enable()
logger = None
logger = logging.getLogger()
class QbittorrentMetricsCollector():
@@ -27,7 +29,6 @@ class QbittorrentMetricsCollector():
def __init__(self, config):
self.config = config
self.torrents = None
self.client = Client(
host=config["host"],
port=config["port"],
@@ -36,11 +37,6 @@ class QbittorrentMetricsCollector():
)
def collect(self):
try:
self.torrents = self.client.torrents.info()
except Exception as e:
logger.error(f"Couldn't get server info: {e}")
metrics = self.get_qbittorrent_metrics()
for metric in metrics:
@@ -65,46 +61,46 @@ class QbittorrentMetricsCollector():
return metrics
def get_qbittorrent_status_metrics(self):
response = {}
version = ""
# Fetch data from API
try:
response = self.client.transfer.info
version = self.client.app.version
self.torrents = self.client.torrents.info()
except Exception as e:
logger.error(f"Couldn't get server info: {e}")
response = None
version = ""
return [
{
"name": "up",
"value": response is None,
"name": f"{self.config['metrics_prefix']}_up",
"value": bool(response),
"labels": {"version": version},
"help": "Whether if server is alive or not",
},
{
"name": "connected",
"name": f"{self.config['metrics_prefix']}_connected",
"value": response.get("connection_status", "") == "connected",
"help": "Whether if server is connected or not",
},
{
"name": "firewalled",
"name": f"{self.config['metrics_prefix']}_firewalled",
"value": response.get("connection_status", "") == "firewalled",
"help": "Whether if server is under a firewall or not",
},
{
"name": "dht_nodes",
"name": f"{self.config['metrics_prefix']}_dht_nodes",
"value": response.get("dht_nodes", 0),
"help": "DHT nodes connected to",
},
{
"name": "dl_info_data",
"name": f"{self.config['metrics_prefix']}_dl_info_data",
"value": response.get("dl_info_data", 0),
"help": "Data downloaded this session (bytes)",
"type": "counter"
},
{
"name": "up_info_data",
"name": f"{self.config['metrics_prefix']}_up_info_data",
"value": response.get("up_info_data", 0),
"help": "Data uploaded this session (bytes)",
"type": "counter"
@@ -114,19 +110,15 @@ class QbittorrentMetricsCollector():
def get_qbittorrent_torrent_tags_metrics(self):
try:
categories = self.client.torrent_categories.categories
torrents = self.client.torrents.info()
except Exception as e:
logger.error(f"Couldn't fetch categories: {e}")
categories = None
if not categories:
return []
if not self.torrents:
logger.error(f"Couldn't fetch torrent info: {e}")
return []
metrics = []
categories.Uncategorized = AttrDict({'name': 'Uncategorized', 'savePath': ''})
for category in categories:
category_torrents = [t for t in self.torrents if t['category'] == category]
category_torrents = [t for t in torrents if t['category'] == category or (category == "Uncategorized" and t['category'] == "")]
for status in self.TORRENT_STATUSES:
status_prop = f"is_{status}"
@@ -134,7 +126,7 @@ class QbittorrentMetricsCollector():
t for t in category_torrents if getattr(TorrentStates, status_prop).fget(TorrentStates(t['state']))
]
metrics.append({
"name": "torrents_count",
"name": f"{self.config['metrics_prefix']}_torrents_count",
"value": len(status_torrents),
"labels": {
"status": status,
@@ -148,49 +140,66 @@ class QbittorrentMetricsCollector():
class SignalHandler():
def __init__(self):
self.shutdown = False
self.shutdownCount = 0
# Register signal handler
signal.signal(signal.SIGINT, self._on_signal_received)
signal.signal(signal.SIGTERM, self._on_signal_received)
def is_shutting_down(self):
return self.shutdown
return self.shutdownCount > 0
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")
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():
config = {
"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")
}
# Register signal handler
signal_handler = SignalHandler()
# Init logger
# Init logger so it can be used
logHandler = logging.StreamHandler()
formatter = jsonlogger.JsonFormatter(
"%(asctime) %(levelname) %(message)",
datefmt="%Y-%m-%d %H:%M:%S"
)
logHandler.setFormatter(formatter)
logger = logging.getLogger()
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"])
# Register signal handler
signal_handler = SignalHandler()
if not config["host"]:
logger.error("No host specified, please set QBITTORRENT_HOST environment variable")
sys.exit(1)
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)
# Register our custom collector

View File

@@ -6,18 +6,18 @@ with open("README.md", "r") as fh:
setup(
name='prometheus-qbittorrent-exporter',
packages=['qbittorrent_exporter'],
version='1.0.0',
version='1.2.0',
long_description=long_description,
long_description_content_type="text/markdown",
description='Prometheus exporter for qbittorrent',
author='Esteban Sanchez',
author_email='esteban.sanchez@gmail.com',
url='https://github.com/esanchezm/prometheus-qbittorrent-exporter',
download_url='https://github.com/spreaker/prometheus-qbittorrent-exporter/archive/1.0.0.tar.gz',
download_url='https://github.com/esanchezm/prometheus-qbittorrent-exporter/archive/1.1.0.tar.gz',
keywords=['prometheus', 'qbittorrent'],
classifiers=[],
python_requires='>=3',
install_requires=['qbittorrent-api==2020.9.9', '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={
'console_scripts': [
'qbittorrent-exporter=qbittorrent_exporter.exporter:main',