Pour une application Laravel extérieure à CONTROL (FiaVia, MESYS, un projet client).
# .env de l'application surveillée INOWATCH_BEAT_URL=https://messagerie.inovertix.com/api/inowatch/beat INOWATCH_BEAT_KEY=INOWTC_HB_…
// config/services.php 'inowatch' => [ 'beat_url' => env('INOWATCH_BEAT_URL'), 'beat_key' => env('INOWATCH_BEAT_KEY'), ],
<?php namespace App\Support; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; /** * INOWATCH — preuve de vie d'une tâche vers CONTROL. * * Ne lève JAMAIS et n'échoue jamais la tâche qu'il observe : un battement raté produit * un silence, et le silence est précisément ce qu'INOWATCH sait signaler. Faire échouer * une sauvegarde parce que la supervision est injoignable serait exactement l'inverse de * ce qu'on cherche. */ class Inowatch { public static function beat(string $slug, bool $failed = false, ?int $durationMs = null): void { $url = config('services.inowatch.beat_url'); $key = config('services.inowatch.beat_key'); // Application déployée sans surveillance (local, préproduction) : on ne fait rien. if (blank($url) || blank($key)) { return; } try { Http::timeout(5) ->withHeaders(['X-Beat-Key' => $key]) ->post(rtrim($url, '/')."/{$slug}", array_filter([ 'failed' => $failed ?: null, 'duration_ms' => $durationMs, ])); } catch (\Throwable $e) { Log::info('INOWATCH: battement non transmis — '.$e->getMessage()); } } }
Plutôt que d'appeler le helper dans chaque commande, branchez-vous sur les événements du planificateur — c'est exactement ce que fait CONTROL pour lui-même, et cela met sous surveillance toutes les tâches existantes sans en modifier une seule.
// app/Providers/AppServiceProvider.php — boot() use App\Support\Inowatch; use Illuminate\Console\Events\ScheduledTaskFailed; use Illuminate\Console\Events\ScheduledTaskFinished; use Illuminate\Support\Facades\Event; use Illuminate\Support\Str; Event::listen([ScheduledTaskFinished::class, ScheduledTaskFailed::class], function ($event) { $command = $event->task->command ?? null; // Les tâches en closure n'ont pas de signature stable : on les ignore. if (! is_string($command)) { return; } // « "/usr/bin/php" "artisan" fiavia:sync --force » → « fiavia:sync » $signature = collect(preg_split('/\s+/', trim($command))) ->map(fn ($part) => trim($part, "'\"")) ->first(fn ($part) => str_contains($part, ':')); if ($signature === null) { return; } Inowatch::beat( Str::slug(str_replace(':', '-', $signature)), failed: $event instanceof ScheduledTaskFailed, durationMs: isset($event->runtime) ? (int) round($event->runtime * 1000) : null, ); });
Déclarez ensuite dans CONTROL un heartbeat par tâche, avec le slug produit par ce code (fiavia-sync pour fiavia:sync).
fiavia-sync
fiavia:sync
public function handle(): int { $started = microtime(true); try { $this->synchroniser(); } catch (\Throwable $e) { Inowatch::beat('fiavia-sync', failed: true); throw $e; } Inowatch::beat('fiavia-sync', durationMs: (int) round((microtime(true) - $started) * 1000)); return self::SUCCESS; }
Même mécanique que dans CONTROL : une tâche planifiée met en file un job trivial, et c'est l'exécution du job qui bat. C'est la seule preuve fiable qu'un worker tourne — une file vide et une file morte se ressemblent, et une file morte se remplit lentement.
// app/Jobs/QueueHeartbeat.php class QueueHeartbeat implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public int $tries = 1; public function __construct() { // Visez la file qui porte VRAIMENT votre travail : prouver qu'un worker // quelconque tourne a moins de valeur que prouver que celui-là tourne. $this->onQueue('default'); } public function handle(): void { Inowatch::beat('fiavia-queue'); } } // routes/console.php Schedule::job(new QueueHeartbeat)->everyFiveMinutes();
Déclarez le heartbeat fiavia-queue avec une cadence de 5 minutes.
fiavia-queue
Ajoutez-le comme moniteur dans /inowatch/moniteurs, avec un mot-clé attendu. Le heartbeat prouve que les tâches tournent ; le moniteur prouve que le site répond. Les deux sont nécessaires : une application peut parfaitement servir ses pages pendant que son planificateur est mort — c'est même la panne la plus courante, et la plus discrète.
/inowatch/moniteurs