Add healthcheck
This commit is contained in:
@@ -9,3 +9,7 @@ MYSQL_ROOT_PASSWORD=root_password
|
|||||||
COLLECT_INTERVAL_SECONDS=900
|
COLLECT_INTERVAL_SECONDS=900
|
||||||
APP_TIMEZONE=Europe/London
|
APP_TIMEZONE=Europe/London
|
||||||
FLASK_SECRET_KEY=replace-with-a-random-string
|
FLASK_SECRET_KEY=replace-with-a-random-string
|
||||||
|
|
||||||
|
# Optional: healthcheck.io-style heartbeat URL
|
||||||
|
# On success: GET <url> | On failure: POST <url>/fail
|
||||||
|
HEALTHCHECK_URL=https://health.pattinson.org/ping/2009942a-9877-411e-89b4-3d17382c8286
|
||||||
|
|||||||
+26
-1
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -73,6 +75,27 @@ def save_reading(device: Device, status: dict[str, Any]) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _ping_healthcheck(success: bool, detail: str | None = None) -> None:
|
||||||
|
url = config.healthcheck_url
|
||||||
|
if not url:
|
||||||
|
logger.debug("Healthcheck ping skipped: HEALTHCHECK_URL not configured")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
if success:
|
||||||
|
logger.debug("Pinging healthcheck (success): %s", url)
|
||||||
|
urllib.request.urlopen(url, timeout=10) # noqa: S310
|
||||||
|
logger.info("Healthcheck ping sent successfully")
|
||||||
|
else:
|
||||||
|
fail_url = f"{url}/fail"
|
||||||
|
logger.debug("Pinging healthcheck (fail): %s — %s", fail_url, detail)
|
||||||
|
payload = (detail or "").encode()
|
||||||
|
req = urllib.request.Request(fail_url, data=payload, method="POST")
|
||||||
|
urllib.request.urlopen(req, timeout=10) # noqa: S310
|
||||||
|
logger.info("Healthcheck fail ping sent: %s", detail)
|
||||||
|
except Exception as exc: # noqa: BLE001
|
||||||
|
logger.warning("Healthcheck ping failed: %s", exc)
|
||||||
|
|
||||||
|
|
||||||
def collect_once(client: SwitchBotClient) -> None:
|
def collect_once(client: SwitchBotClient) -> None:
|
||||||
logger.info("Starting collection cycle")
|
logger.info("Starting collection cycle")
|
||||||
devices = sync_devices(client)
|
devices = sync_devices(client)
|
||||||
@@ -106,8 +129,10 @@ def main() -> int:
|
|||||||
started = time.monotonic()
|
started = time.monotonic()
|
||||||
try:
|
try:
|
||||||
collect_once(client)
|
collect_once(client)
|
||||||
except Exception:
|
_ping_healthcheck(True)
|
||||||
|
except Exception as exc:
|
||||||
logger.exception("Collector cycle failed")
|
logger.exception("Collector cycle failed")
|
||||||
|
_ping_healthcheck(False, str(exc))
|
||||||
elapsed = time.monotonic() - started
|
elapsed = time.monotonic() - started
|
||||||
time.sleep(max(1, interval - elapsed))
|
time.sleep(max(1, interval - elapsed))
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class Config:
|
|||||||
collect_interval_seconds: int = int(os.getenv("COLLECT_INTERVAL_SECONDS", "900"))
|
collect_interval_seconds: int = int(os.getenv("COLLECT_INTERVAL_SECONDS", "900"))
|
||||||
app_timezone: str = os.getenv("APP_TIMEZONE", "Europe/London")
|
app_timezone: str = os.getenv("APP_TIMEZONE", "Europe/London")
|
||||||
flask_secret_key: str = os.getenv("FLASK_SECRET_KEY", "dev-only-secret")
|
flask_secret_key: str = os.getenv("FLASK_SECRET_KEY", "dev-only-secret")
|
||||||
|
healthcheck_url: str | None = os.getenv("HEALTHCHECK_URL")
|
||||||
|
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
<section class="hero">
|
<section class="hero">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">Today so far</p>
|
<p class="eyebrow">Today so far</p>
|
||||||
<h1>Temperature dashboard</h1>
|
<h1>Vet Temperatures</h1>
|
||||||
<p class="muted">Local timezone: {{ timezone }}. Collector interval: {{ collect_interval_seconds // 60 }} min.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<a class="button" href="/reports">Make report</a>
|
<a class="button" href="/reports">Make report</a>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ services:
|
|||||||
SWITCHBOT_SECRET: ${SWITCHBOT_SECRET:-}
|
SWITCHBOT_SECRET: ${SWITCHBOT_SECRET:-}
|
||||||
COLLECT_INTERVAL_SECONDS: ${COLLECT_INTERVAL_SECONDS:-900}
|
COLLECT_INTERVAL_SECONDS: ${COLLECT_INTERVAL_SECONDS:-900}
|
||||||
APP_TIMEZONE: ${APP_TIMEZONE:-Europe/London}
|
APP_TIMEZONE: ${APP_TIMEZONE:-Europe/London}
|
||||||
|
HEALTHCHECK_URL: ${HEALTHCHECK_URL:-}
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|||||||
Reference in New Issue
Block a user