diff --git a/README.md b/README.md index 5cef9af..6a593d3 100644 --- a/README.md +++ b/README.md @@ -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 @@ -14,47 +14,54 @@ pip3 install prometheus-qbittorrent-exporter 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 ``` - job_name: "qbittorrent_exporter" static_configs: - - targets: ['yourqbittorrentexporter:port'] + - targets: ['yourimmichexporter:port'] ``` The application reads configuration using environment variables: -| Environment variable | Default | Description | -| -------------------- | ------------- | ----------- | -| `QBITTORRENT_HOST` | | qbittorrent server hostname | -| `QBITTORRENT_PORT` | | qbittorrent server port | -| `QBITTORRENT_USER` | `""` | qbittorrent username | -| `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 | +| Environment variable | Default | Description | +|----------------------|----------|----------------------------------------------------| +| `IMMICH_HOST` | | immich server hostname | +| `IMMICH_PORT` | | immich server port | +| `EXPORTER_PORT` | `8000` | Exporter listening port | +| `EXPORTER_LOG_LEVEL` | `INFO` | Log level. One of: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` | +| `METRICS_PREFIX` | `immich` | Prefix to add to all the 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 | -| --------------------------------------------------- | -------- | ---------------- | -| `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 | -| `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_info_version_number` | `pings server and passes version number with the use of labels={version}` | +| `immich_server_info_diskAvailable` | `available space on disk` | +| `immich_server_info_totalDiskSize` | `total disk size` | +| `immich_server_info_diskUse` | `disk space used by your system` | +| `immich_server_info_diskUsagePercentage` | `same as above but in percentage` | + +| `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 diff --git a/grafana/README.md b/grafana/README.md index 10383b6..8663e06 100644 --- a/grafana/README.md +++ b/grafana/README.md @@ -2,7 +2,20 @@ ## 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 diff --git a/grafana/immich-exporter-dashboard.json b/grafana/dashboard.json similarity index 50% rename from grafana/immich-exporter-dashboard.json rename to grafana/dashboard.json index bef4e4d..ec0c863 100644 --- a/grafana/immich-exporter-dashboard.json +++ b/grafana/dashboard.json @@ -127,7 +127,7 @@ }, "editorMode": "code", "exemplar": false, - "expr": "immich_version_number{version=\"1.50.1\"}", + "expr": "immich_server_info_version_number", "instant": true, "legendFormat": "{{version}}", "range": false, @@ -193,7 +193,7 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_user_count", + "expr": "immich_server_stats_user_count", "legendFormat": "__auto", "range": true, "refId": "A" @@ -255,7 +255,7 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "sum(immich_usage_by_user)", + "expr": "sum(immich_server_stats_usage_by_users)", "legendFormat": "__auto", "range": true, "refId": "A" @@ -284,7 +284,7 @@ } ] }, - "unit": "decgbytes" + "unit": "decbytes" }, "overrides": [] }, @@ -317,7 +317,7 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_totalDiskSize{}", + "expr": "immich_server_info_totalDiskSize", "legendFormat": "__auto", "range": true, "refId": "A" @@ -346,7 +346,7 @@ } ] }, - "unit": "decgbytes" + "unit": "decbytes" }, "overrides": [] }, @@ -379,7 +379,7 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_diskUse{}", + "expr": "immich_server_info_diskUse", "legendFormat": "__auto", "range": true, "refId": "A" @@ -450,7 +450,7 @@ }, "editorMode": "code", "exemplar": false, - "expr": "immich_diskUsagePercentage", + "expr": "immich_server_info_diskUsagePercentage", "format": "time_series", "instant": true, "interval": "", @@ -462,6 +462,136 @@ "title": "remaining disk size", "type": "stat" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 3 + }, + "hideTimeOverride": true, + "id": 24, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "increase(immich_server_stats_useage_growth[24h])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/d", + "title": "Daily data growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 3 + }, + "hideTimeOverride": true, + "id": 26, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_useage_growth[7d])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/w", + "title": "Weekly data growth", + "type": "stat" + }, { "datasource": { "type": "prometheus", @@ -485,9 +615,9 @@ "overrides": [] }, "gridPos": { - "h": 10, - "w": 8, - "x": 0, + "h": 8, + "w": 12, + "x": 12, "y": 3 }, "id": 8, @@ -520,15 +650,265 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_usage_by_user", + "expr": "immich_server_stats_usage_by_users", "legendFormat": "{{firstName}}", "range": true, "refId": "A" } ], - "title": "usage in GB", + "title": "disk usage by user", "type": "piechart" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 7 + }, + "hideTimeOverride": true, + "id": 28, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_useage_growth[30h])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/M", + "title": "Monthly data growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 7 + }, + "hideTimeOverride": true, + "id": 30, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_useage_growth[365d])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/y", + "title": "Yealy data growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 11 + }, + "hideTimeOverride": true, + "id": 32, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_photos_growth[24h])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/d", + "title": "daily photos growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 11 + }, + "hideTimeOverride": true, + "id": 34, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_photos_growth[7d])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/w", + "title": "weekly photos growth", + "type": "stat" + }, { "datasource": { "type": "prometheus", @@ -552,10 +932,10 @@ "overrides": [] }, "gridPos": { - "h": 10, - "w": 8, - "x": 8, - "y": 3 + "h": 8, + "w": 12, + "x": 12, + "y": 11 }, "id": 4, "options": { @@ -567,7 +947,7 @@ "placement": "right", "showLegend": true }, - "pieType": "donut", + "pieType": "pie", "reduceOptions": { "calcs": [ "lastNotNull" @@ -588,7 +968,7 @@ }, "editorMode": "code", "exemplar": false, - "expr": "immich_photos_by_user", + "expr": "immich_server_stats_photos_by_users", "instant": false, "legendFormat": "{{firstName}}", "range": true, @@ -606,37 +986,34 @@ "fieldConfig": { "defaults": { "color": { - "mode": "palette-classic" + "mode": "thresholds" }, - "custom": { - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - } - }, - "mappings": [] + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } }, "overrides": [] }, "gridPos": { - "h": 10, - "w": 8, - "x": 16, - "y": 3 + "h": 4, + "w": 6, + "x": 0, + "y": 15 }, - "id": 6, + "hideTimeOverride": true, + "id": 36, "options": { - "displayLabels": [ - "percent" - ], - "legend": { - "displayMode": "list", - "placement": "right", - "showLegend": true, - "values": [] - }, - "pieType": "donut", + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", "reduceOptions": { "calcs": [ "lastNotNull" @@ -644,11 +1021,9 @@ "fields": "", "values": false }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "textMode": "auto" }, + "pluginVersion": "9.4.3", "targets": [ { "datasource": { @@ -656,14 +1031,78 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_videos_by_user", - "legendFormat": "{{firstName}}", + "expr": "increase(immich_server_stats_photos_growth[4w])", + "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "videos", - "type": "piechart" + "timeFrom": "now/M", + "title": "Monthly photos growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 15 + }, + "hideTimeOverride": true, + "id": 38, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_photos_growth[52w])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/y", + "title": "yearly photos growth", + "type": "stat" }, { "datasource": { @@ -725,11 +1164,12 @@ "overrides": [] }, "gridPos": { - "h": 8, - "w": 12, + "h": 7, + "w": 24, "x": 0, - "y": 13 + "y": 19 }, + "hideTimeOverride": true, "id": 22, "options": { "legend": { @@ -750,15 +1190,335 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_photos_by_user{job=\"prometheus\"}", + "expr": "immich_server_stats_photos_by_users", "legendFormat": "{{firstName}}", "range": true, "refId": "A" } ], - "title": "Photos Growth", + "timeFrom": "now/M", + "title": "Monthly Photos Growth", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 26 + }, + "hideTimeOverride": true, + "id": 42, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_videos_growth[1d])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/d", + "title": "daily video growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 26 + }, + "hideTimeOverride": true, + "id": 44, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_videos_growth[4w])\r\n", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/M", + "title": "Monthly videos growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 26 + }, + "id": 6, + "options": { + "displayLabels": [ + "percent" + ], + "legend": { + "displayMode": "list", + "placement": "right", + "showLegend": true, + "values": [] + }, + "pieType": "pie", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "immich_server_stats_videos_by_users", + "legendFormat": "{{firstName}}", + "range": true, + "refId": "A" + } + ], + "title": "videos", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 0, + "y": 30 + }, + "hideTimeOverride": true, + "id": 40, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_videos_growth[1w])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/w", + "title": "weekly videos growth", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 6, + "x": 6, + "y": 30 + }, + "hideTimeOverride": true, + "id": 46, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.3", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "increase(immich_server_stats_videos_growth[52w])\r\n", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "now/y", + "title": "Panel Title", + "type": "stat" + }, { "datasource": { "type": "prometheus", @@ -818,9 +1578,9 @@ }, "gridPos": { "h": 8, - "w": 12, - "x": 12, - "y": 13 + "w": 24, + "x": 0, + "y": 34 }, "id": 20, "options": { @@ -842,17 +1602,18 @@ "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", - "expr": "immich_videos_by_user ", + "expr": "immich_server_stats_videos_by_users", "legendFormat": "{{firstName}}", "range": true, "refId": "A" } ], + "timeFrom": "now/M", "title": "Videos growth", "type": "timeseries" } ], - "refresh": "", + "refresh": "5s", "revision": 1, "schemaVersion": 38, "style": "dark", @@ -868,6 +1629,6 @@ "timezone": "", "title": "immich", "uid": "ZWWp3aa4k", - "version": 4, + "version": 7, "weekStart": "" } \ No newline at end of file diff --git a/grafana/screenshot.png b/grafana/screenshot.png index bd1266d..b4ff953 100644 Binary files a/grafana/screenshot.png and b/grafana/screenshot.png differ diff --git a/immich_exporter/exporter.py b/immich_exporter/exporter.py index 835ac3f..9e75a6e 100644 --- a/immich_exporter/exporter.py +++ b/immich_exporter/exporter.py @@ -4,7 +4,6 @@ import sys import signal import faulthandler - import requests from prometheus_client import start_http_server from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY @@ -16,7 +15,7 @@ faulthandler.enable() logger = logging.getLogger() -class QbittorrentMetricsCollector(): +class ImmichMetricsCollector: def __init__(self, config): self.config = config @@ -43,13 +42,14 @@ class QbittorrentMetricsCollector(): metrics = [] metrics.extend(self.get_immich_server_version_number()) 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 - def get_immich_users_stat(self): - + def get_immich_users_stat_growth(self): + global response_user_stats try: endpoint_user_stats = "/api/server-info/stats" response_user_stats = requests.request( @@ -61,25 +61,58 @@ class QbittorrentMetricsCollector(): except requests.exceptions.RequestException as 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"]) + photos_growth_total = 0 + videos_growth_total = 0 + usage_growth_total = 0 - #userCount - metrics.append( + for x in range(0, userCount): + 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, "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"] - for x in range(0,userCount): + + for x in range(0, userCount): 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'], "labels": { "firstName": userData[x]["userFirstName"], @@ -87,14 +120,14 @@ class QbittorrentMetricsCollector(): }, "help": f"Number of photos by user {userData[x]['userFirstName']} " - } - ) + } + ) - #videos - for x in range(0,userCount): + # videos + for x in range(0, userCount): 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'], "labels": { "firstName": userData[x]["userFirstName"], @@ -102,13 +135,13 @@ class QbittorrentMetricsCollector(): }, "help": f"Number of photos by user {userData[x]['userFirstName']} " - } - ) - #usage - for x in range(0,userCount): + } + ) + # usage + for x in range(0, userCount): 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']), "labels": { "firstName": userData[x]["userFirstName"], @@ -116,8 +149,8 @@ class QbittorrentMetricsCollector(): }, "help": f"Number of photos by user {userData[x]['userFirstName']} " - } - ) + } + ) return metrics @@ -133,30 +166,26 @@ class QbittorrentMetricsCollector(): except requests.exceptions.RequestException as e: logger.error(f"Couldn't get server version: {e}") - - - - return [ { - "name": f"{self.config['metrics_prefix']}_diskAvailable", + "name": f"{self.config['metrics_prefix']}_server_info_diskAvailable", "value": (response_server_info.json()["diskAvailableRaw"]), "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"]), "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"]), "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"]), "help": "disk usage in percent", # "type": "counter" @@ -166,6 +195,7 @@ class QbittorrentMetricsCollector(): def get_immich_server_version_number(self): server_version_endpoint = "/api/server-info/version" + response_server_version = "" try: @@ -177,15 +207,14 @@ class QbittorrentMetricsCollector(): except requests.exceptions.RequestException as e: logger.error(f"Couldn't get server version: {e}") - server_version_number = ( str(response_server_version.json()["major"]) + "." + - str(response_server_version.json()["minor"]) + "." + - str(response_server_version.json()["patch"]) - ) - + server_version_number = (str(response_server_version.json()["major"]) + "." + + str(response_server_version.json()["minor"]) + "." + + str(response_server_version.json()["patch"]) + ) return [ { - "name": f"{self.config['metrics_prefix']}_version_number", + "name": f"{self.config['metrics_prefix']}_server_info_version_number", "value": bool(server_version_number), "help": "server version number", "labels": {"version": server_version_number} @@ -194,14 +223,14 @@ class QbittorrentMetricsCollector(): ] def combine_url(self, api_endpoint): + prefix_url = "http://" base_url = self.config["immich_host"] 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 - class SignalHandler(): def __init__(self): self.shutdownCount = 0 @@ -261,13 +290,16 @@ def main(): if not config["immich_host"]: logger.error("No host specified, please set IMMICH_HOST environment variable") 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"]: logger.error("No token specified, please set IMMICH_API_TOKEN environment variable") sys.exit(1) # Register our custom collector logger.info("Exporter is starting up") - REGISTRY.register(QbittorrentMetricsCollector(config)) + REGISTRY.register(ImmichMetricsCollector(config)) # Start server start_http_server(config["exporter_port"])