Moniteurs Heartbeat
Les moniteurs Heartbeat inversent la direction - votre tâche nous indique qu’elle est active. Deux modes : Planifié (intervalle attendu) et Récepteur d’événements (dormant ; ne se déclenche que lorsque les déclencheurs correspondent au payload).
MODES
- Planifié : si aucun battement n’arrive dans l’intervalle configuré, le statut passe à MISS et les alertes se déclenchent.
- Échec explicite planifié : appelez la même URL avec ?status=fail lorsque la tâche s’est exécutée mais a détecté un mauvais résultat. Cela enregistre un battement KO et déclenche immédiatement la gestion de panne.
- Récepteur d’événements : le statut reste dormant. Chaque battement stocke son payload ; les déclencheurs avec conditions l’évaluent. ?status=fail est ignoré en mode Récepteur d’événements car les conditions des déclencheurs décident de ce qui compte.
OK beat:
curl -fsS https://keepitalive.dev/heartbeat/{token}
Explicit failed beat:
curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail"RECETTES PLANIFIÉES
*/5 * * * * curl -fsS https://keepitalive.dev/heartbeat/{token} >/dev/null0 2 * * * /usr/bin/backup.sh && curl -fsS https://keepitalive.dev/heartbeat/{token}curl -fsS "https://keepitalive.dev/heartbeat/{token}?status=fail&payload=disk+full"*/1 * * * * if ping -c 1 -W 2 192.168.1.50 >/dev/null 2>&1; then \
curl -fsS "https://keepitalive.dev/heartbeat/{token}"; \
else \
curl -fsS -X POST "https://keepitalive.dev/heartbeat/{token}?status=fail" \
-d "unreachable"; \
fi:: C:\scripts\keepalive_heartbeat.bat
@echo off
curl -fsS https://keepitalive.dev/heartbeat/{token} >nul 2>&1
:: schtasks /create /tn "KEEPitALIVE" /tr C:\scripts\keepalive_heartbeat.bat /sc minute /mo 5PLANIFICATIONS CRON
Un heartbeat simple attend un signal toutes les N secondes, ce qui ne convient pas a une tache planifiee sur un calendrier : une sauvegarde en semaine seulement semble avoir 65 heures de retard chaque dimanche. Definissez plutot une expression cron et l'echeance devient la prochaine execution prevue plus la tolerance : rien n'est en retard tant que rien n'etait attendu. Choisissez le fuseau horaire ou tourne la tache : la planification y est evaluee, ce qui maintient une tache de 03:00 a 03:00 lors des changements d'heure. L'expression fixe aussi le cout, car elle determine combien d'executions par heure nous attendons. L'URL de reception ne change pas ; seule change notre attente du moment ou le signal est du.
# Give the monitor the same schedule the job runs on.
0 3 * * 1-5 # 03:00, weekdays only
*/15 * * * * # every 15 minutes
0 3 1 * * # 03:00 on the 1st of the month
@daily # midnight
# Pick the zone the job runs in, not the server's: that is
# what keeps 03:00 at 03:00 when the clocks change.CODES DE SORTIE
Ajoutez le code de sortie de votre tâche à l'URL et le beat se signale tout seul : 0 est une réussite, toute autre valeur un échec. C'est préférable à un enchaînement avec && , qui n'envoie rien du tout quand la tâche échoue - le moniteur ne tombe alors que plus tard, à la fenêtre manquée, au lieu du moment de la panne. Le code est enregistré sur le beat, donc une condition de déclencheur peut router selon le type d'échec : payload.exit_code == 137 (tué pour cause de mémoire) peut ouvrir une panne tandis que payload.exit_code == 1 se contente de notifier. Les codes doivent être compris entre 0 et 255 ; vous pouvez aussi utiliser /fail si vous n'avez pas de code sous la main.
# $? is the exit status of the job that just ran
0 2 * * * /usr/bin/backup.sh; curl -fsS https://keepitalive.dev/heartbeat/{token}/$?
# Send the job output too, so the alert says why it failed.
# Capture it first: in a pipeline $? is the last command's status,
# not the job's.
0 2 * * * out=$(/usr/bin/backup.sh 2>&1); \
curl -fsS --data-binary "$out" https://keepitalive.dev/heartbeat/{token}/$?DURÉE D'EXÉCUTION
Pinguez /start au début de la tâche et envoyez un signal normal à la fin : le temps écoulé est enregistré sur le signal sous duration_ms, ce qui permet d'alerter sur une exécution lente - payload.duration_ms > 300000 repère une sauvegarde ayant dépassé cinq minutes même si elle a fini par réussir. /start est totalement facultatif : un unique signal final constitue déjà un rapport complet, et la détection des absences comme les codes de sortie fonctionnent sans lui. L'envoyer apporte exactement deux choses : une durée enregistrée et la détection d'une exécution qui ne se termine jamais. Un /start seul ne rapporte rien et ne change pas l'état du moniteur : une tâche qui démarre puis se bloque doit continuer à manquer sa fenêtre. Un second /start alors qu'une exécution est encore ouverte est refusé, car deux démarrages sans fin entre eux signifient que le premier n'a jamais rapporté ; le refus est levé dès que cette exécution a dépassé son délai. Les exécutions ouvertes depuis plus de 24 heures sont abandonnées.
# /start is optional. Without it you still get miss detection
# and the exit status - just no duration and no stuck-run alert:
0 2 * * * /usr/bin/backup.sh; curl -fsS https://keepitalive.dev/heartbeat/{token}/$?
# With it, the run is timed and a job that never finishes is caught:
0 2 * * * curl -fsS https://keepitalive.dev/heartbeat/{token}/start; \
/usr/bin/backup.sh; \
curl -fsS https://keepitalive.dev/heartbeat/{token}/$?EXÉCUTIONS BLOQUÉES
Si une tâche appelle /start puis se bloque, rien n'est encore en retard : le début n'a rien rafraîchi, donc le moniteur semble correct jusqu'à l'échéance du prochain beat. C'est précisément le problème quand on chronomètre une tâche : une exécution bloquée est exactement la panne que vous vouliez détecter. Une exécution qui n'a pas envoyé son beat final tombe donc dès qu'elle dépasse l'intervalle plus la fenêtre de grâce, avec une erreur indiquant depuis combien de temps elle tourne. Il n'y a pas de réglage distinct de durée maximale : un beat en retard et une exécution bloquée posent la même question — combien de temps attend-on — donc la grâce déjà définie répond aux deux.
PAYLOADS
Envoyez du JSON par POST vers votre URL de heartbeat pour signaler des métriques système ou des champs arbitraires. La carte Dernier rapport sur la page de détail du moniteur affiche le dernier payload en JSON formaté lorsque c’est possible, avec un repli en texte brut.
curl -fsS -X POST https://keepitalive.dev/heartbeat/{token} \
-H 'Content-Type: application/json' \
-d '{"job":"nightly-backup","status":"ok","rows":42850}'RÉCEPTEUR D’ÉVÉNEMENTS : MAINTENANCE CI/CD
Le mode Récepteur d’événements est utile pour les pipelines de déploiement. Créez un déclencheur de payload avec la condition payload.status == "maintenance" et la classification d’incident maintenance. Envoyez un payload de maintenance avant le déploiement et un payload running/ok dans une étape finale if: always(). L’incident s’ouvre tant que l’expression correspond et se ferme lorsque le prochain payload accepté l’efface.
- name: Start maintenance
run: |
curl -fsS -X POST "$KEEPITALIVE_HEARTBEAT_URL" \
-H "Content-Type: application/json" \
-d '{"status":"maintenance","source":"github-actions","sha":"${{github.sha}}"}'
- name: Deploy
run: ./deploy.sh
- name: End maintenance
if: always()
run: |
curl -fsS -X POST "$KEEPITALIVE_HEARTBEAT_URL" \
-H "Content-Type: application/json" \
-d '{"status":"running","source":"github-actions","sha":"${{github.sha}}"}'Les récepteurs d’événements limitent les payloads acceptés selon l’intervalle du moniteur. Pour des tâches de déploiement très courtes, utilisez le plus petit intervalle autorisé par votre forfait ou retardez le payload final jusqu’à ce que l’intervalle soit écoulé. Si la source devient silencieuse, KEEPitALIVE ne présume pas un rétablissement.
#!/bin/bash
URL="https://keepitalive.dev/heartbeat/{token}"
CPU=$(top -bn1 | grep "Cpu" | awk '{print $2}')
RAM=$(free -m | awk '/Mem/{printf "%.1f", $3/$2*100}')
DISK=$(df -h / | awk 'NR==2{print $5}')
curl -fsS -X POST $URL \
-H "Content-Type: application/json" \
-d "{\"hostname\":\"$(hostname)\",\"cpu\":\"$CPU%\",\"ram\":\"$RAM%\",\"disk\":\"$DISK\"}"$url = "https://keepitalive.dev/heartbeat/{token}"
$os = Get-CimInstance Win32_OperatingSystem
$body = @{
hostname = $env:COMPUTERNAME
cpu = "{0:N1}%" -f (Get-CimInstance Win32_Processor).LoadPercentage
ram = "{0:N1}%" -f ((1 - $os.FreePhysicalMemory / $os.TotalVisibleMemorySize) * 100)
} | ConvertTo-Json
Invoke-RestMethod -Uri $url -Method POST -Body $body -ContentType "application/json"