Aller au contenu

Monitoring étudiant temps réel

perfshop-monitoring est un composant Node.js autonome qui expose un dashboard HTML temps réel. Il joue deux rôles distincts mais complémentaires :

  1. Collecteur Docker — il interroge le socket Docker pour exposer en format Prometheus les métriques CPU / mémoire / réseau / I/O de chaque conteneur PerfShop
  2. Dashboard HTML — il sert une page web qui affiche en direct ces métriques ainsi que celles du backend Spring Boot, dans une version volontairement plus simple et plus pédagogique que Grafana

Sources

monitoring/src/server.js, monitoring/public/index.html, monitoring/package.json, monitoring/Dockerfile

Pourquoi un dashboard HTML distinct ?

Grafana est une excellente plateforme d'observabilité mais son interface peut submerger les étudiants débutants. perfshop-monitoring offre une vue simplifiée :

  • Un seul écran, pas de navigation
  • Des jauges et des graphiques de base (pas de PromQL à écrire)
  • Centré sur les ressources des conteneurs PerfShop plutôt que sur l'hôte
  • Mise à jour automatique toutes les quelques secondes, sans interaction

Le dashboard complet Grafana reste disponible pour les étudiants plus avancés et les formateurs — voir Grafana.

Stack

Composant Version Rôle
Node.js 20 Alpine Runtime
Express 4.18 Serveur HTTP
node-fetch 3.3 Appels HTTP vers le backend
Docker socket /var/run/docker.sock Lecture directe des stats conteneurs

Le conteneur monte le socket Docker en lecture pour pouvoir interroger l'API /containers/json et /containers/{name}/stats du démon.

Configuration par variables d'environnement

Variable Défaut Rôle
PORT 3001 Port d'écoute HTTP
APP_METRICS_URL http://perfshop-app:8080/actuator/prometheus Endpoint Prometheus du backend
DOCKER_SOCKET /var/run/docker.sock Chemin du socket Docker
HEAPDUMP_URL http://perfshop-app:9090/actuator/heapdump Endpoint heap dump (port management interne)
PERFSHOP_API_INTERNAL http://perfshop-app:8080 Backend interne pour le proxy /api/admin/login
PUBLIC_API_URL http://localhost:8080 Injecté dans window.__CONFIG__
PUBLIC_MONITORING_URL http://localhost:3001 Idem
PUBLIC_GRAFANA_URL http://localhost:3002 Idem
PUBLIC_CHAOS_URL http://localhost:3003 Idem
PERFSHOP_LANG fr Langue injectée dans window.__CONFIG__.LANG

Les cinq variables PUBLIC_* sont regroupées dans un objet PUBLIC_CONFIG injecté dans chaque page HTML servie via un <script> inline (route / et /config.js). Cela permet au JavaScript client d'appeler les bons endpoints en respectant la config de déploiement.

Conteneurs surveillés

Le tableau CONTAINERS_TO_WATCH est codé en dur dans server.js :

const CONTAINERS_TO_WATCH = [
  'perfshop-frontend', 'perfshop-app', 'perfshop-db', 'perfshop-monitoring'
];

Ce sont les quatre conteneurs critiques du cœur PerfShop. La fonction resolveContainerNames() gère intelligemment le nommage Docker Compose : elle accepte les préfixes de projet (perfshop-perfshop-app) et les séparateurs (- ou _) pour fonctionner aussi bien sur NAS que sur Docker Desktop. Elle s'exécute au démarrage puis toutes les 60 secondes pour rester à jour en cas de redémarrage de conteneurs.

Endpoints exposés

/metrics — format Prometheus

Retourne en texte brut des métriques format Prometheus que Prometheus scrape et stocke. Deux familles de métriques sont exposées :

Métriques Docker (par conteneur surveillé) :

Métrique Type Labels
docker_container_cpu_percent gauge container
docker_container_mem_usage_bytes gauge container
docker_container_mem_limit_bytes gauge container
docker_container_mem_percent gauge container
docker_container_net_rx_bytes counter container
docker_container_net_tx_bytes counter container
docker_container_io_read_bytes counter container
docker_container_io_write_bytes counter container
docker_container_pids gauge container

La mesure mémoire exclut le cache disque (memory.stats.cache ou inactive_file) pour afficher la mémoire réellement utilisée et non le simple cache de pages Linux.

Métriques client browser (Chaos Frontend) — uniquement si reçues dans les 10 secondes précédentes :

Métrique Rôle
perfshop_client_fps Frames par seconde mesurées dans le navigateur
perfshop_client_heap_used_mb Heap JS utilisée en MB
perfshop_client_long_tasks_per_sec Nombre de long tasks (>50 ms) par seconde
perfshop_client_fetch_req_per_sec Fetch requests lancées par seconde
perfshop_client_dom_node_count Nombre de nœuds DOM
perfshop_client_cpu_worker_active 1 si un Web Worker CPU tourne
perfshop_client_last_received_timestamp Timestamp Unix du dernier push

Ces métriques sont poussées par chaos-agent.js depuis les navigateurs via POST /api/chaos/client-metrics toutes les 2 secondes. perfshop-monitoring sert de pont entre les navigateurs et Prometheus (Prometheus ne scrape pas directement les navigateurs).

/api/docker/all

Retourne les stats Docker cachées sous forme JSON pour le dashboard HTML. Un cache TTL de 5 secondes évite de surcharger le démon Docker.

/api/docker/stats?container=NAME

Retourne les stats d'un conteneur spécifique en JSON.

/api/prometheus-raw

Proxy transparent vers le backend Spring Boot (/actuator/prometheus). Le dashboard client utilise ce endpoint avec son propre parser parsePrometheus() plutôt que d'aller directement sur Spring Boot, pour deux raisons : contourner les problèmes cross-origin et bénéficier du timeout à 5 secondes qui évite de bloquer le dashboard si le backend est sous chaos.

/api/chaos/client-metrics

  • POST : reçoit les métriques browser poussées par chaos-agent.js. Chaque champ est validé par type (typeof x === 'number') pour éviter les injections.
  • GET : retourne les dernières métriques reçues avec un flag stale si plus de 10 secondes se sont écoulées sans push.

/api/heapdump

Proxy vers l'endpoint /actuator/heapdump du backend (port management interne 9090, non exposé publiquement). Génère un nom de fichier horodaté et envoie le binaire .hprof au client. Timeout de 60 secondes — un heap dump peut prendre ~30 secondes sur une JVM chargée. Le dashboard dispose d'un widget (heapdump-widget.html) qui déclenche ce téléchargement.

/api/admin/login

Proxy vers /api/admin/login du backend. Cet alias contourne les restrictions cross-origin et permet au dashboard de se connecter en admin sans que le navigateur ait à appeler directement perfshop-app:8080. Le backend retourne 402 si une licence manque, et le proxy propage ce code fidèlement.

Dashboard HTML

Le dashboard est servi sur /. index.html est lu à chaque requête et le script <script>window.__CONFIG__ = {...}</script> est injecté juste avant </head> — cela évite d'avoir à reconstruire l'image quand une URL publique change.

Le dashboard affiche :

  • Quatre cartes conteneurs (frontend, backend, db, monitoring) avec CPU / mémoire / réseau
  • Des graphiques JVM temps réel (heap, threads, pools Tomcat / Hikari)
  • Un panneau Chaos Métier (compteurs d'anomalies détectées)
  • Un panneau Chaos Sécurité (compteurs d'attaques)
  • Une zone de téléchargement de heap dump
  • Une barre latérale de login admin

Le parser parsePrometheus() (client-side) extrait des métriques comme jvm_memory_used_bytes{area="heap"}, process_cpu_usage, jvm_threads_live_threads, tomcat_threads_busy_threads, hikaricp_connections_active etc. pour alimenter les graphiques.

Architecture JavaScript — duplication éliminée

Le dashboard est scindé en trois couches claires :

Fichier Rôle Lignes
public/js/monitoring-charts.js Utilitaires purs : mkCfg, mkDS, addPt, parsePrometheus, histogramQuantile, diffBuckets, formatBytes, switchTab ~140
public/js/monitoring-core.js Logique partagée étudiant/admin : construction des Chart.js communs, fetchers (fetchBackendMetrics, fetchAppCpu, fetchClientMetrics, fetchDockerStats, fetchGeneral), triggerHeapDump, bindGlobalPause, openGrafana. Exposé via window.PerfShopMonitoring. ~530
public/js/monitoring-app.js Wrapper étudiant (~60 lignes) — câble monitoring-core.js sur les cartes de la page index.html et définit le polling. Contient uniquement la logique du banner backend spécifique à l'étudiant. ~60
public/admin/js/admin-app.js Wrapper admin (~660 lignes) — câble monitoring-core.js plus le code spécifique admin : beChart13 (intensités chaos), panneaux Chaos Backend/Frontend, Scripting, Chaos Métier, Chaos Sécurité, Chaos Fonctionnel, modale détails tokens, login/logout. ~660

Avant ce refactor, monitoring-app.js et admin-app.js partageaient ~278 lignes verbatim (50 % du fichier étudiant strictement identique à des fonctions admin). Un bug de label Chart.js ou un catch(e){} silencieux devait être corrigé deux fois. monitoring-core.js unifie ce socle : une seule source de vérité, une seule correction à appliquer.

Contrat d'API de monitoring-core.js :

const M = window.PerfShopMonitoring;

const isPaused = M.bindGlobalPause();      // bouton #global-pause-btn → getter booléen
const charts   = M.createSharedCharts({ beChart13: /* admin-only optional */ });
const state    = M.createSharedState();    // compteurs EMA, buckets précédents, etc.

M.fetchBackendMetrics(charts, state, isPaused);   // retourne { text, tomcatBusy, dbPending }
M.fetchAppCpu(charts, isPaused, { verboseMissing: true });
M.fetchClientMetrics(charts, isPaused, { updateBanner: true });  // student only
M.fetchDockerStats(charts, state, isPaused);
M.fetchGeneral(charts, isPaused);
M.triggerHeapDump();
M.openGrafana(selectElement, GRAFANA_URL);

Les flags d'option (verboseMissing, updateBanner) portent les petites différences de comportement entre étudiant et admin sans multiplier les fonctions. L'ordre de chargement des scripts est strict :

<script src="/config.js"></script>                              <!-- window.__CONFIG__ -->
<script src="https://.../chart.umd.min.js"></script>            <!-- Chart.js global -->
<script src="/js/i18n.js"></script>                             <!-- _t, loadI18n, I18N -->
<script src="/js/monitoring-charts.js"></script>                <!-- mkDS, mkCfg, parsePrometheus -->
<script src="/js/monitoring-core.js"></script>                  <!-- window.PerfShopMonitoring -->
<script src="/js/monitoring-app.js"></script>                   <!-- OU /admin/js/admin-app.js -->

Internationalisation

Le monitoring dispose de son propre dossier public/i18n/ avec fr.json et en.json. La langue est propagée via window.__CONFIG__.LANG (valeur de PERFSHOP_LANG). Le chargement du dictionnaire suit le même pattern que chaos-admin : loader async, _t(), applyI18n() déclaratif.

public/js/i18n.js expose cinq attributs HTML déclaratifs :

Attribut Effet
data-i18n="key" el.textContent = _t('key') (ou document.title si l'élément est <title>)
data-i18n-html="key" el.innerHTML = _t('key') — contenu statique de confiance, aucun input utilisateur
data-i18n-placeholder="key" el.placeholder = _t('key')
data-i18n-title="key" el.title = _t('key') (tooltip)
data-i18n-label="key" el.label = _t('key') — pour <optgroup>

Ajouter une langue se résume à déposer un fichier public/i18n/<code>.json avec les mêmes clés que fr.json, puis à positionner PERFSHOP_LANG=<code> dans l'environnement du conteneur. Aucune modification de code requise. Le chargement applique une cascade de détection : window.__CONFIG__.LANGnavigator.languagefr. Si le fichier demandé est manquant, un fallback vers fr.json est appliqué automatiquement avec un avertissement en console.

Voir Outils annexes.

Différences avec Grafana

Critère perfshop-monitoring Grafana
Public cible Étudiants débutants, démonstration rapide Étudiants avancés, formateurs, QA
Interaction Lecture seule, pas de config Dashboards personnalisables, PromQL
Couverture Conteneurs PerfShop + JVM + Chaos Toute la stack d'observabilité
Source de données Docker socket + /actuator/prometheus direct Prometheus (historique) + Loki + Tempo
Persistance Aucune (temps réel uniquement) Stockage Prometheus (15j par défaut)

L'un ne remplace pas l'autre : le dashboard HTML est une porte d'entrée vers l'observabilité, Grafana est l'outil d'analyse des incidents.