Compare commits

..

10 commits

Author SHA1 Message Date
friendlyFriend4000
c23ad910df
Merge pull request #3 from Allram/master
Update dashboard-immich.json and some translations/naming
2023-08-22 22:03:13 +02:00
friendlyFriend4000
f6d95209c0
Merge branch 'master' into master 2023-08-22 22:01:18 +02:00
friendly Friend
3c6d7ef8ae -display exporter version number on startup 2023-07-28 17:23:59 +02:00
friendly Friend
edc8701419 -fixed ssl error.
-more transparent error codes
2023-07-26 08:24:24 +02:00
friendly Friend
23e2a7b939 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	immich_exporter/exporter.py
#	setup.py
2023-07-26 08:24:12 +02:00
friendly Friend
e07e9fb459 -fixed ssl error.
-more transparent error codes
2023-07-26 08:22:58 +02:00
friendly Friend
7601be0f1b what's new:
easier console log output to troubleshoot errors

fixed issue where exporter did not start when immich wasn't running after CA backup
2023-07-22 21:51:46 +02:00
friendly Friend
5a07ffdf0d what's new:
easier console log output to troubleshoot errors

fixed issue where exporter did not start when immich wasn't running after CA backup
2023-07-22 21:50:59 +02:00
Vegard Fladby
99c9b84f40 Further optimization of Dashboard:
- Formated text to make it consistent
- Changed hours/days/month/year to use 1d/1w/4w/1y
- Changed Weekly data growth to use 1w to show the last 7 days.
- Changed Monthly data growth to use 4w to show the last 4 weeks.
- Changed Yearly data growth to use 1y to show the last 1y.
- Added Photos growth last year visual
- Added Videos growth last year visual
- Corrected some spelling errors in 'expr' so that the values are now calculated correctly
2023-07-20 20:14:28 +02:00
Vegard Fladby
513856beff Further edit of readme
- Added better description on UnRaid template
- Added Immich_api_token to readme
- Populated default Immich_port 8080 in Dockerfile
- Clarified that it's the Immich Proxy that you need to connect to, not Immich Server
2023-07-20 18:15:05 +02:00
13 changed files with 378 additions and 86 deletions

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

14
.idea/deployment.xml Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
<serverData>
<paths name="//TOWER">
<serverdata>
<mappings>
<mapping local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
</serverData>
</component>
</project>

View file

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

4
.idea/misc.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (2)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/prometheus-immich-exporter.iml" filepath="$PROJECT_DIR$/.idea/prometheus-immich-exporter.iml" />
</modules>
</component>
</project>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.10 (2)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -14,7 +14,7 @@ RUN pip3 install .
ENV IMMICH_API_TOKEN=""
ENV IMMICH_HOST=""
ENV IMMICH_PORT=""
ENV IMMICH_PORT="8080"
#has to be EXPORT_PORT 8000 or else it does not work, same applies to the env file
ENV EXPORTER_PORT="8000"
ENV EXPORTER_LOG_LEVEL="INFO"

View file

@ -5,26 +5,14 @@ A prometheus exporter for Immich. Get metrics from a server and offers them in a
## How to use it
You can install this exporter with the following command:
```bash
pip3 install prometheus-qbittorrent-exporter
```
Then you can run it with
Here is an example docker run command
```
immich-exporter
```
Another option is to run it in a docker container. Here is an example docker run command
```
docker run -e IMMICH_PORT=8010 -e IMMICH_HOST=192.168.178.1 -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 friendlyfriend/prometheus-immich-exporter
```
Add this to your prometheus.yml
```
- job_name: "immich_exporter"
- job_name: "Immich_exporter"
static_configs:
- targets: ['yourimmichexporter:port']
```
@ -32,8 +20,9 @@ The application reads configuration using environment variables:
| Environment variable | Default | Description |
|----------------------|----------|----------------------------------------------------|
| `IMMICH_HOST` | | immich server hostname |
| `IMMICH_PORT` | | immich server port |
| `IMMICH_HOST` | | Immich proxy url |
| `IMMICH_PORT` | `8080` | Immich proxy port |
| `IMMICH_API_TOKEN` | | Immich API token, created from Immich dashboard |
| `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 |

View file

@ -199,7 +199,7 @@
"refId": "A"
}
],
"title": "user count",
"title": "User count",
"type": "stat"
},
{
@ -261,7 +261,7 @@
"refId": "A"
}
],
"title": "disk space used by Immich",
"title": "Disk space used by Immich",
"type": "stat"
},
{
@ -323,7 +323,7 @@
"refId": "A"
}
],
"title": "total disk size",
"title": "Total disk size",
"type": "stat"
},
{
@ -385,7 +385,7 @@
"refId": "A"
}
],
"title": "total disk size usage",
"title": "Total disk size usage",
"type": "stat"
},
{
@ -451,7 +451,7 @@
"refId": "A"
}
],
"title": "remaining disk size",
"title": "Remaining disk size",
"type": "stat"
},
{
@ -484,7 +484,7 @@
"x": 0,
"y": 3
},
"hideTimeOverride": true,
"hideTimeOverride": false,
"id": 24,
"options": {
"colorMode": "background",
@ -509,7 +509,7 @@
},
"editorMode": "code",
"exemplar": false,
"expr": "increase(immich_server_stats_usage_growth[24h])",
"expr": "increase(immich_server_stats_usage_growth[1d])",
"instant": false,
"legendFormat": "__auto",
"range": true,
@ -550,7 +550,7 @@
"x": 6,
"y": 3
},
"hideTimeOverride": true,
"hideTimeOverride": false,
"id": 26,
"options": {
"colorMode": "background",
@ -574,13 +574,13 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "increase(immich_server_stats_useage_growth[7d])",
"expr": "increase(immich_server_stats_usage_growth[1w])",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"timeFrom": "now/w",
"timeFrom": "1w",
"title": "Weekly data growth",
"type": "stat"
},
@ -648,7 +648,7 @@
"refId": "A"
}
],
"title": "disk usage by user",
"title": "Disk usage by user",
"type": "piechart"
},
{
@ -681,7 +681,7 @@
"x": 0,
"y": 7
},
"hideTimeOverride": true,
"hideTimeOverride": false,
"id": 28,
"options": {
"colorMode": "background",
@ -705,13 +705,13 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "increase(immich_server_stats_usage_growth[30h])",
"expr": "increase(immich_server_stats_usage_growth[4w])",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"timeFrom": "now/M",
"timeFrom": "1M",
"title": "Monthly data growth",
"type": "stat"
},
@ -745,7 +745,7 @@
"x": 6,
"y": 7
},
"hideTimeOverride": true,
"hideTimeOverride": false,
"id": 30,
"options": {
"colorMode": "background",
@ -769,14 +769,14 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "increase(immich_server_stats_useage_growth[365d])",
"expr": "increase(immich_server_stats_usage_growth[1y])",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"timeFrom": "now/y",
"title": "Yealy data growth",
"timeFrom": "1y",
"title": "Yearly data growth",
"type": "stat"
},
{
@ -837,7 +837,7 @@
}
],
"timeFrom": "now/d",
"title": "daily photos growth",
"title": "Daily photos growth",
"type": "stat"
},
{
@ -897,8 +897,8 @@
"refId": "A"
}
],
"timeFrom": "now/w",
"title": "weekly photos growth",
"timeFrom": "1w",
"title": "Weekly photos growth",
"type": "stat"
},
{
@ -1029,7 +1029,7 @@
"refId": "A"
}
],
"timeFrom": "now/M",
"timeFrom": "1M",
"title": "Monthly photos growth",
"type": "stat"
},
@ -1086,14 +1086,14 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "increase(immich_server_stats_photos_growth[52w])",
"expr": "increase(immich_server_stats_photos_growth[1y])",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"timeFrom": "now/y",
"title": "yearly photos growth",
"timeFrom": "1y",
"title": "Yearly photos growth",
"type": "stat"
},
{
@ -1160,7 +1160,7 @@
"x": 0,
"y": 19
},
"hideTimeOverride": true,
"hideTimeOverride": false,
"id": 22,
"options": {
"legend": {
@ -1187,8 +1187,103 @@
"refId": "A"
}
],
"timeFrom": "now/M",
"title": "Monthly Photos Growth",
"timeFrom": "1M",
"title": "Photos Growth",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "amount of photos over time",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "right",
"axisSoftMin": 9000,
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 30,
"gradientMode": "opacity",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineStyle": {
"fill": "solid"
},
"lineWidth": 1,
"pointSize": 1,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 24,
"x": 0,
"y": 26
},
"hideTimeOverride": false,
"id": 47,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "immich_server_stats_photos_by_users",
"legendFormat": "{{firstName}}",
"range": true,
"refId": "A"
}
],
"timeFrom": "1y",
"title": "Photos Growth",
"type": "timeseries"
},
{
@ -1206,8 +1301,7 @@
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
"color": "green"
}
]
}
@ -1218,7 +1312,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 26
"y": 33
},
"hideTimeOverride": true,
"id": 42,
@ -1251,7 +1345,7 @@
}
],
"timeFrom": "now/d",
"title": "daily video growth",
"title": "Daily video growth",
"type": "stat"
},
{
@ -1269,8 +1363,7 @@
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
"color": "green"
}
]
}
@ -1281,7 +1374,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 26
"y": 33
},
"hideTimeOverride": true,
"id": 40,
@ -1313,8 +1406,8 @@
"refId": "A"
}
],
"timeFrom": "now/w",
"title": "weekly videos growth",
"timeFrom": "1w",
"title": "Weekly videos growth",
"type": "stat"
},
{
@ -1342,7 +1435,7 @@
"h": 8,
"w": 12,
"x": 12,
"y": 26
"y": 33
},
"id": 6,
"options": {
@ -1381,7 +1474,7 @@
"refId": "A"
}
],
"title": "videos",
"title": "Videos",
"type": "piechart"
},
{
@ -1410,7 +1503,7 @@
"h": 4,
"w": 6,
"x": 0,
"y": 30
"y": 37
},
"hideTimeOverride": true,
"id": 44,
@ -1428,7 +1521,7 @@
},
"textMode": "auto"
},
"pluginVersion": "9.4.3",
"pluginVersion": "10.0.2",
"targets": [
{
"datasource": {
@ -1442,7 +1535,7 @@
"refId": "A"
}
],
"timeFrom": "now/M",
"timeFrom": "1M",
"title": "Monthly videos growth",
"type": "stat"
},
@ -1472,7 +1565,7 @@
"h": 4,
"w": 6,
"x": 6,
"y": 30
"y": 37
},
"hideTimeOverride": true,
"id": 46,
@ -1490,7 +1583,7 @@
},
"textMode": "auto"
},
"pluginVersion": "9.4.3",
"pluginVersion": "10.0.2",
"targets": [
{
"datasource": {
@ -1498,14 +1591,14 @@
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "increase(immich_server_stats_videos_growth[52w])\r\n",
"expr": "increase(immich_server_stats_videos_growth[1y])\r\n",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"timeFrom": "now/y",
"title": "Panel Title",
"timeFrom": "1y",
"title": "Yearly video growth",
"type": "stat"
},
{
@ -1568,7 +1661,7 @@
"h": 8,
"w": 24,
"x": 0,
"y": 34
"y": 41
},
"id": 20,
"options": {
@ -1596,7 +1689,99 @@
"refId": "A"
}
],
"timeFrom": "now/M",
"timeFrom": "1M",
"title": "Videos growth",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "amount of videos over time",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "right",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 30,
"gradientMode": "opacity",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineStyle": {
"fill": "solid"
},
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "normal"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 49
},
"id": 48,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"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"
}
],
"timeFrom": "1y",
"title": "Videos growth",
"type": "timeseries"
}
@ -1617,6 +1802,6 @@
"timezone": "",
"title": "immich",
"uid": "ZWWp3aa4k",
"version": 1,
"version": 9,
"weekStart": ""
}

View file

@ -39,6 +39,7 @@ class ImmichMetricsCollector:
yield prom_metric
def get_immich_metrics(self):
metrics = []
metrics.extend(self.get_immich_server_version_number())
metrics.extend(self.get_immich_server_info())
@ -47,6 +48,7 @@ class ImmichMetricsCollector:
return metrics
def get_immich_users_stat_growth(self):
try:
@ -210,24 +212,27 @@ class ImmichMetricsCollector:
]
def get_immich_server_version_number(self):
# Requesting immich_server_number serves two purposes. As the name says it returns the version number
# 1. get version the full server version number
# 2. check if immich api key is correct
# throwing connectionRefused exception usually means that immich isn't running
server_version_endpoint = "/api/server-info/version"
response_server_version = ""
for i in range(0, 360):
while True:
try:
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"]}
)
except requests.exceptions.RequestException as e:
logger.error(f"Couldn't get server version: {e}")
continue
break
response_server_version = requests.request(
"GET",
self.combine_url(server_version_endpoint),
headers={'Accept': 'application/json',
"x-api-key": self.config["token"]}
)
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"]) + "." +
@ -253,6 +258,7 @@ class ImmichMetricsCollector:
return combined_url
# test
class SignalHandler():
def __init__(self):
self.shutdownCount = 0
@ -284,6 +290,58 @@ def get_config_value(key, default=""):
return os.environ.get(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",
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?")
if 0 <= counter <= 60:
time.sleep(1)
elif 11 <= counter <= 300:
time.sleep(15)
elif counter > 300:
time.sleep(60)
continue
break
logger.info(f"Found immich up and running at " + immichHost + ":" + immichPort + ".")
logger.info(f"Attempting to connect")
time.sleep(1)
logger.info("Exporter v1.0.6")
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}
)
except requests.exceptions.RequestException as e:
logger.error(f"CONNECTION ERROR. Possible API key error")
logger.error({e})
time.sleep(3)
continue
logger.info(f"Connected to immich successfully")
break
def main():
# Init logger so it can be used
logHandler = logging.StreamHandler()
@ -321,15 +379,21 @@ def main():
# Register our custom collector
logger.info("Exporter is starting up")
check_server_up(config["immich_host"], config["immich_port"])
check_immich_api_key(config["immich_host"], config["immich_port"], config["token"])
REGISTRY.register(ImmichMetricsCollector(config))
# Start server
start_http_server(config["exporter_port"])
logger.info(
f"Exporter listening on port {config['exporter_port']}"
)
while not signal_handler.is_shutting_down():
time.sleep(1)
logger.info("Exporter has shutdown")

View file

@ -6,7 +6,7 @@ with open("README.md", "r") as fh:
setup(
name='prometheus-immich-exporter',
packages=['immich_exporter'],
version='1.0.1',
version='1.0.6',
long_description=long_description,
long_description_content_type="text/markdown",
description='Prometheus exporter for immich',

View file

@ -18,11 +18,11 @@
<PostArgs/>
<CPUset/>
<DateInstalled>1678876858</DateInstalled>
<DonateText>If I made your day a little bit brighter, consider donating.</DonateText>
<DonateText>If you like my work, consider supporting me.</DonateText>
<DonateLink>https://www.paypal.com/donate/?hosted_button_id=DPDKED3T3BFV8</DonateLink>
<Requires/>
<Config Name="Host Port 1" Target="8000" Default="8028" Mode="tcp" Description="" Type="Port" Display="always" Required="false" Mask="false">8028</Config>
<Config Name="Immich_exporter_port" Target="8000" Default="8000" Mode="tcp" Description="" Type="Port" Display="always" Required="false" Mask="false">8000</Config>
<Config Name="immich api token" Target="IMMICH_API_TOKEN" Default="" Mode="" Description="You need to log into to your immich admin account&#13;&#10;and create an api token in your settings" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="immich_host" Target="IMMICH_HOST" Default="" Mode="" Description="example: 192.168.178.2" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="immich port" Target="IMMICH_PORT" Default="" Mode="" Description="" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="immich_host" Target="IMMICH_HOST" Default="" Mode="" Description="Address to Immich_proxy, example: 192.168.178.2" Type="Variable" Display="always" Required="false" Mask="false"/>
<Config Name="immich port" Target="IMMICH_PORT" Default="8080" Mode="" Description="Port for Immich_proxy" Type="Variable" Display="always" Required="false" Mask="false"/>
</Container>