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 :
- 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
- 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 parchaos-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 flagstalesi 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__.LANG → navigator.language → fr. 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.