Merge pull request #13

feat: add load average and virtual memory metrics
This commit is contained in:
friendlyFriend4000
2024-06-19 00:46:10 +02:00
committed by GitHub
7 changed files with 586 additions and 200 deletions

View File

@@ -3,7 +3,7 @@ FROM alpine:3.18.4
# Installing required packages
FROM alpine/doctl
ENV PYTHONUNBUFFERED=1
RUN apk add --update --no-cache python3 py-pip && ln -sf python3 /usr/bin/python
RUN apk add --update --no-cache python3 py-pip gcc python3-dev musl-dev linux-headers && ln -sf python3 /usr/bin/python
#RUN python3 -m ensurepip
RUN pip3 install --break-system-packages --no-cache --upgrade pip setuptools

View File

@@ -5,17 +5,37 @@ A prometheus exporter for Immich. Get metrics from a server and offers them in a
## How to use it
Here is an example docker run command
Here is an example docker run command
```
docker run -e IMMICH_PORT=8080 -e IMMICH_HOST=192.168.178.1 -e IMMICH_API_TOKEN=TOKEN -p 8000:8000 friendlyfriend/prometheus-immich-exporter
docker run -e IMMICH_PORT=8080 -e IMMICH_HOST=192.168.178.1 -e IMMICH_API_TOKEN=<TOKEN> -p 8000:8000 alexf007/prometheus-immich-exporter
```
Or you can add the following to your immich docker-compose.yaml
```
immich-exporter:
image: alexf007/prometheus-immich-exporter
container_name: immich_exporter
environment:
- IMMICH_PORT=3001
- IMMICH_HOST=immich-server
- IMMICH_API_TOKEN=<TOKEN>
ports:
- 8000:8000
restart: unless-stopped
```
Add this to your prometheus.yml
```
- job_name: "Immich_exporter"
- job_name: "immich_exporter"
static_configs:
- targets: ['yourimmichexporter:port']
```
In case if you run prometheus from the same docker-compose you can use this job
```
- job_name: immich_exporter
static_configs:
- targets: ['immich-exporter:8000']
```
The application reads configuration using environment variables:
| Environment variable | Default | Description |
@@ -35,12 +55,12 @@ These are the metrics this program exports, assuming the `METRICS_PREFIX` is `im
| `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` |
| `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 ` |
@@ -50,7 +70,13 @@ These are the metrics this program exports, assuming the `METRICS_PREFIX` is `im
| `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` |
| `metric name` | `description` |
|---------------------------------------|------------------------------------------------------------------------|
| `immich_system_info_loadAverage` | `array of load average (1m, 5m 15m)` |
| `immich_system_info_memory` | `array of memory states (Total, Available, Percent, Used, Free)` |
| `immich_system_cpu_usage` | `Representing the current system-wide CPU utilization as a percentage` |
## Screenshot

View File

@@ -2,7 +2,7 @@
## Import
To import the dashboard into your grafana, download the [dashboard.json](https://github.com/friendlyFriend4000/prometheus-immich-exporter/raw/master/grafana/dashboard-immich.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](./dashboard-immich.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

View File

@@ -113,12 +113,14 @@
"fields": "",
"values": false
},
"showPercentChange": false,
"text": {
"titleSize": 6
},
"textMode": "name"
"textMode": "name",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -183,9 +185,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -245,9 +249,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -307,9 +313,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -369,9 +377,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -431,9 +441,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -454,6 +466,309 @@
"title": "Remaining disk size",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "amount of CPU usage over time",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 3
},
"id": 51,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "immich_system_info_loadAverage",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "{{period}}",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Load Average",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 8,
"y": 3
},
"id": 53,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "immich_system_info_cpu_usage",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "CPU usage",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "CPU Usage",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "Free memory over time",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "bytes"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 16,
"y": 3
},
"id": 52,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "immich_system_info_memory{type=\"Free\"}",
"fullMetaSearch": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "{{type}}",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Free memory",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
@@ -482,7 +797,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 3
"y": 11
},
"hideTimeOverride": false,
"id": 24,
@@ -498,9 +813,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -549,7 +866,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 3
"y": 11
},
"hideTimeOverride": false,
"id": 26,
@@ -565,9 +882,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -611,7 +930,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 3
"y": 11
},
"id": 8,
"options": {
@@ -619,7 +938,7 @@
"value"
],
"legend": {
"displayMode": "list",
"displayMode": "table",
"placement": "right",
"showLegend": true,
"values": [
@@ -636,6 +955,7 @@
"values": false
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
@@ -684,7 +1004,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 7
"y": 15
},
"hideTimeOverride": false,
"id": 28,
@@ -700,9 +1020,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -748,7 +1070,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 7
"y": 15
},
"hideTimeOverride": false,
"id": 30,
@@ -764,9 +1086,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -810,7 +1134,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 11
"y": 19
},
"hideTimeOverride": false,
"id": 32,
@@ -826,9 +1150,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -873,7 +1199,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 11
"y": 19
},
"hideTimeOverride": false,
"id": 34,
@@ -889,9 +1215,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -936,7 +1264,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 11
"y": 19
},
"id": 4,
"options": {
@@ -961,6 +1289,7 @@
"values": false
},
"tooltip": {
"maxHeight": 600,
"mode": "multi",
"sort": "none"
}
@@ -1012,7 +1341,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 15
"y": 23
},
"hideTimeOverride": false,
"id": 36,
@@ -1028,9 +1357,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -1076,7 +1407,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 15
"y": 23
},
"hideTimeOverride": false,
"id": 38,
@@ -1092,9 +1423,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -1124,6 +1457,7 @@
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
@@ -1175,7 +1509,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 19
"y": 27
},
"hideTimeOverride": false,
"id": 22,
@@ -1187,6 +1521,7 @@
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
@@ -1220,6 +1555,7 @@
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
@@ -1271,7 +1607,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 26
"y": 34
},
"hideTimeOverride": false,
"id": 47,
@@ -1283,6 +1619,7 @@
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
@@ -1332,7 +1669,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 33
"y": 41
},
"hideTimeOverride": false,
"id": 42,
@@ -1348,9 +1685,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -1396,7 +1735,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 33
"y": 41
},
"hideTimeOverride": false,
"id": 40,
@@ -1412,9 +1751,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -1457,7 +1798,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 33
"y": 41
},
"id": 6,
"options": {
@@ -1465,7 +1806,7 @@
"percent"
],
"legend": {
"displayMode": "list",
"displayMode": "table",
"placement": "right",
"showLegend": true,
"values": [
@@ -1482,6 +1823,7 @@
"values": false
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
@@ -1530,7 +1872,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 37
"y": 45
},
"hideTimeOverride": false,
"id": 44,
@@ -1546,9 +1888,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -1594,7 +1938,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 37
"y": 45
},
"hideTimeOverride": false,
"id": 46,
@@ -1610,9 +1954,11 @@
"fields": "",
"values": false
},
"textMode": "auto"
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "10.1.5",
"pluginVersion": "11.0.0",
"targets": [
{
"datasource": {
@@ -1642,6 +1988,7 @@
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
@@ -1693,7 +2040,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 41
"y": 49
},
"hideTimeOverride": false,
"id": 49,
@@ -1705,6 +2052,7 @@
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
@@ -1738,6 +2086,7 @@
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
@@ -1789,7 +2138,7 @@
"h": 7,
"w": 24,
"x": 0,
"y": 48
"y": 56
},
"hideTimeOverride": false,
"id": 50,
@@ -1801,6 +2150,7 @@
"showLegend": true
},
"tooltip": {
"maxHeight": 600,
"mode": "single",
"sort": "none"
}
@@ -1825,20 +2175,20 @@
],
"refresh": "30s",
"revision": 1,
"schemaVersion": 38,
"style": "dark",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-5m",
"from": "now-15m",
"to": "now"
},
"timeRangeUpdatedDuringEditOrView": false,
"timepicker": {},
"timezone": "",
"title": "immich",
"uid": "ZWWp3aa4k",
"version": 31,
"version": 2,
"weekStart": ""
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 234 KiB

View File

@@ -5,6 +5,8 @@ import signal
import faulthandler
import requests
import psutil
from prometheus_client import start_http_server
from prometheus_client.core import GaugeMetricFamily, CounterMetricFamily, REGISTRY
import logging
@@ -16,12 +18,22 @@ logger = logging.getLogger()
class ImmichMetricsCollector:
def __init__(self, config):
self.config = config
def collect(self):
def request(self, endpoint):
response = requests.request(
"GET",
self.combine_url(endpoint),
headers={
"Accept": "application/json",
"x-api-key": self.config["token"]
}
)
return response
def collect(self):
metrics = self.get_immich_metrics()
for metric in metrics:
@@ -40,43 +52,61 @@ class ImmichMetricsCollector:
logger.info(prom_metric)
def get_immich_metrics(self):
metrics = []
metrics.extend(self.get_immich_server_version_number())
metrics.extend(self.get_immich_storage())
metrics.extend(self.get_immich_users_stat)
metrics.extend(self.get_immich_users_stat_growth())
metrics.extend(self.get_immich_users_stat())
metrics.extend(self.get_system_stats())
return metrics
def get_immich_users_stat_growth(self):
def get_immich_users_stat(self):
try:
endpoint_user_stats = "/api/server-info/statistics"
response_user_stats = requests.request(
"GET",
self.combine_url(endpoint_user_stats),
headers={'Accept': 'application/json',
"x-api-key": self.config["token"]}
)
response_user_stats = self.request(endpoint_user_stats).json()
except requests.exceptions.RequestException as e:
logger.error(f"Couldn't get server version: {e}")
logger.error(f"API ERROR: can't get server statistic: {e}")
user_data = response_user_stats.json()["usageByUser"]
# photos growth gauge
user_count = len(response_user_stats.json()["usageByUser"])
user_data = response_user_stats["usageByUser"]
user_count = len(response_user_stats["usageByUser"])
photos_growth_total = 0
videos_growth_total = 0
usage_growth_total = 0
metrics = []
for x in range(0, user_count):
photos_growth_total += user_data[x]["photos"]
# total video growth
videos_growth_total += user_data[x]["videos"]
# total disk growth
usage_growth_total += user_data[x]["usage"]
metrics.append(
{
"name": f"{self.config['metrics_prefix']}_server_stats_photos_by_users",
"value": user_data[x]['photos'],
"labels": {"firstName": user_data[x]["userName"].split()[0]},
"help": f"Number of photos by user {user_data[x]['userName'].split()[0]} "
}
)
metrics.append(
{
"name": f"{self.config['metrics_prefix']}_server_stats_videos_by_users",
"value": user_data[x]['videos'],
"labels": {"firstName": user_data[x]["userName"].split()[0]},
"help": f"Number of photos by user {user_data[x]['userName'].split()[0]} "
}
)
metrics.append(
{
"name": f"{self.config['metrics_prefix']}_server_stats_usage_by_users",
"value": (user_data[x]['usage']),
"labels": {
"firstName": user_data[x]["userName"].split()[0],
},
"help": f"Number of photos by user {user_data[x]['userName'].split()[0]} "
}
)
return [
metrics += [
{
"name": f"{self.config['metrics_prefix']}_server_stats_user_count",
"value": user_count,
@@ -96,113 +126,39 @@ class ImmichMetricsCollector:
"name": f"{self.config['metrics_prefix']}_server_stats_usage_growth",
"value": usage_growth_total,
"help": "videos counter that is added or removed"
}
},
]
@property
def get_immich_users_stat(self):
global response_user_stats
try:
endpoint_user_stats = "/api/server-info/statistics"
response_user_stats = requests.request(
"GET",
self.combine_url(endpoint_user_stats),
headers={'Accept': 'application/json',
"x-api-key": self.config["token"]}
)
except requests.exceptions.RequestException as e:
logger.error(f"API ERROR: can't get server statistic: {e}")
logger.info(f"API TOKEN CORRECT?")
logger.info(f"API ENDPOINT CHANGED?")
metrics = []
# To get the user count an api-endpoint exists but this works too. As a result one less api call is being made
try:
user_count = len(response_user_stats.json()["usageByUser"])
except Exception:
logger.error("Is the Immich api token valid? Traceback:KeyError: 'usageByUser': ")
# json array of all users with stats
# this line throws an error if api token is wrong. if the token is wrong
# or invalid this will return a KeyError : 'usage by user'
user_data = response_user_stats.json()["usageByUser"]
for x in range(0, user_count):
metrics.append(
{
"name": f"{self.config['metrics_prefix']}_server_stats_photos_by_users",
"value": user_data[x]['photos'],
"labels": {
"firstName": user_data[x]["userName"].split()[0],
},
"help": f"Number of photos by user {user_data[x]['userName'].split()[0]} "
}
)
# videos
for x in range(0, user_count):
metrics.append(
{
"name": f"{self.config['metrics_prefix']}_server_stats_videos_by_users",
"value": user_data[x]['videos'],
"labels": {
"firstName": user_data[x]["userName"].split()[0],
},
"help": f"Number of photos by user {user_data[x]['userName'].split()[0]} "
}
)
# usage
for x in range(0, user_count):
metrics.append(
{
"name": f"{self.config['metrics_prefix']}_server_stats_usage_by_users",
"value": (user_data[x]['usage']),
"labels": {
"firstName": user_data[x]["userName"].split()[0],
},
"help": f"Number of photos by user {user_data[x]['userName'].split()[0]} "
}
)
return metrics
def get_immich_storage(self):
try:
endpoint_storage = "/api/server-info/storage"
response_storage = requests.request(
"GET",
self.combine_url(endpoint_storage),
headers={'Accept': 'application/json',
"x-api-key": self.config["token"]}
)
response_storage = self.request(endpoint_storage).json()
except requests.exceptions.RequestException as e:
logger.error(f"Couldn't get storage info: {e}")
response_json = response_storage.json()
return [
{
"name": f"{self.config['metrics_prefix']}_server_info_diskAvailable",
"value": (response_json["diskAvailableRaw"]),
"value": (response_storage["diskAvailableRaw"]),
"help": "Available space on disk",
},
{
"name": f"{self.config['metrics_prefix']}_server_info_totalDiskSize",
"value": (response_json["diskSizeRaw"]),
"value": (response_storage["diskSizeRaw"]),
"help": "total disk size",
# "type": "counter"
},
{
"name": f"{self.config['metrics_prefix']}_server_info_diskUse",
"value": (response_json["diskUseRaw"]),
"value": (response_storage["diskUseRaw"]),
"help": "disk space in use",
# "type": "counter"
},
{
"name": f"{self.config['metrics_prefix']}_server_info_diskUsagePercentage",
"value": (response_json["diskUsagePercentage"]),
"value": (response_storage["diskUsagePercentage"]),
"help": "disk usage in percent",
# "type": "counter"
}
@@ -219,22 +175,15 @@ class ImmichMetricsCollector:
while True:
try:
response_server_version = requests.request(
"GET",
self.combine_url(server_version_endpoint),
headers={'Accept': 'application/json',
"x-api-key": self.config["token"]}
)
response = self.request(server_version_endpoint).json()
except requests.exceptions.RequestException as e:
logger.error(f"Couldn't get server version")
continue
break
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["major"]) + "." + str(response["minor"]) + "." + str(response["patch"])
)
return [
{
@@ -246,11 +195,71 @@ class ImmichMetricsCollector:
}
]
def get_system_stats(self):
loadAvg = os.getloadavg()
virtualMem = psutil.virtual_memory()
cpu = psutil.cpu_percent(interval=1, percpu=False)
return [
{
"name": f"{self.config['metrics_prefix']}_system_info_loadAverage",
"value": loadAvg[0],
"help": "CPU Load average 1m",
"labels": {"period": "1m"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_loadAverage",
"value": loadAvg[1],
"help": "CPU Load average 5m",
"labels": {"period": "5m"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_loadAverage",
"value": loadAvg[2],
"help": "CPU Load average 15m",
"labels": {"period": "15m"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_memory",
"value": virtualMem[0],
"help": "Virtual Memory - Total",
"labels": {"type": "Total"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_memory",
"value": virtualMem[1],
"help": "Virtual Memory - Available",
"labels": {"type": "Available"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_memory",
"value": virtualMem[2],
"help": "Virtual Memory - Percent",
"labels": {"type": "Percent"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_memory",
"value": virtualMem[3],
"help": "Virtual Memory - Used",
"labels": {"type": "Used"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_memory",
"value": virtualMem[4],
"help": "Virtual Memory - Free",
"labels": {"type": "Free"},
},
{
"name": f"{self.config['metrics_prefix']}_system_info_cpu_usage",
"value": cpu,
"help": "Representing the current system-wide CPU utilization as a percentage",
},
]
def combine_url(self, api_endpoint):
prefix_url = "http://"
base_url = self.config["immich_host"]
base_url_port = self.config["immich_port"]
combined_url = prefix_url + base_url + ":" + base_url_port + api_endpoint
combined_url = f"{prefix_url}{base_url}:{base_url_port}{api_endpoint}"
return combined_url
@@ -288,21 +297,19 @@ def get_config_value(key, default=""):
def check_server_up(immichHost, immichPort):
#
counter = 0
while True:
counter = counter + 1
try:
requests.request(
"GET",
"http://" + immichHost + ":" + immichPort + "/api/server-info/ping",
f"http://{immichHost}:{immichPort}/api/server-info/ping",
headers={'Accept': 'application/json'}
)
except requests.exceptions.RequestException as e:
logger.error(f"CONNECTION ERROR. Cannot reach immich at " + immichHost + ":" + immichPort + "."
f"Is immich up and running?")
logger.error(
f"CONNECTION ERROR. Cannot reach immich at {immichHost}:{immichPort}. Is immich up and running?")
if 0 <= counter <= 60:
time.sleep(1)
elif 11 <= counter <= 300:
@@ -311,21 +318,22 @@ def check_server_up(immichHost, immichPort):
time.sleep(60)
continue
break
logger.info(f"Found immich up and running at " + immichHost + ":" + immichPort + ".")
logger.info(f"Attempting to connect to immich")
logger.info(f"Found immich up and running at {immichHost}:{immichPort}.")
logger.info("Attempting to connect to immich")
time.sleep(1)
logger.info("Exporter v1.0.9")
logger.info("Exporter 1.2.0")
def check_immich_api_key(immichHost, immichPort, immichApiKey):
while True:
try:
requests.request(
"GET",
"http://" + immichHost + ":" + immichPort + "/api/server-info/",
headers={'Accept': 'application/json',
"x-api-key": immichApiKey}
f"http://{immichHost}:{immichPort}/api/server-info/",
headers={
"Accept": "application/json",
"x-api-key": immichApiKey
}
)
except requests.exceptions.RequestException as e:
logger.error(f"CONNECTION ERROR. Possible API key error")

View File

@@ -3,10 +3,12 @@ from setuptools import setup
with open("README.md", "r") as fh:
long_description = fh.read()
version = '1.2.0'
setup(
name='prometheus-immich-exporter',
packages=['immich_exporter'],
version='1.1.0',
version=version,
long_description=long_description,
long_description_content_type="text/markdown",
description='Prometheus exporter for immich',
@@ -18,7 +20,7 @@ setup(
keywords=['prometheus', 'immich'],
classifiers=[],
python_requires='>=3',
install_requires=['attrdict==2.0.1', 'prometheus_client==0.19.0 ', 'requests==2.31.0', 'python-json-logger==2.0.7'],
install_requires=['attrdict==2.0.1', 'prometheus_client==0.19.0 ', 'requests==2.31.0', 'python-json-logger==2.0.7', 'psutil==5.9.8'],
entry_points={
'console_scripts': [
'immich_exporter=immich_exporter.exporter:main',