Aller au contenu

Session 19 — JMeter UI : logs en direct (IHM + Grafana/Loki)

Durée : ~4 heures (2 suites)
Contexte : Session focalisée sur l'observabilité des tirs JMeter — logs accessibles à la fois dans l'IHM JMeter UI et dans Grafana/Loki, sans docker-cli (socket Docker uniquement).


🎯 Objectif de la session

Rendre les logs JMeter observables depuis deux points d'entrée :

  • IHM JMeter UI : panneau "Logs en direct" avec auto-refresh, deux colonnes (perfshop-jmeter / perfshop-jmeter-ui)
  • Grafana/Loki : deux panels dans le dashboard JMeter — logs perfshop-jmeter et perfshop-jmeter-ui
  • Contrainte : perfshop-jmeter fait tail -f /dev/null → docker logs vide → solution par volume partagé + fichier statique Promtail

📦 Fichiers modifiés

Fichier Nature
jmeter-ui/src/server.js Endpoint GET /api/logs via socket Docker, -j /jmeter-logs/jmeter.log, try/catch /api/run
jmeter-ui/public/index.html Panneau "Logs en direct", gestion 401 session expirée
promtail/promtail-config.yml Job jmeter-log — lecture fichier statique /jmeter-logs/jmeter.log
grafana/dashboards/dashboard-jmeter.json Panels 105/106/107 — row Logs + panels Loki
docker-compose.yml + docker-compose.desktop.yml Volume ./jmeter/logs:/jmeter-logs sur perfshop-jmeter et perfshop-promtail

🏗️ Architecture retenue

Problème structurel

perfshop-jmeter est un container permanent (tail -f /dev/null) — ses logs Docker sont vides. Deux chemins distincts pour exposer les logs JMeter :

IHM JMeter UI (/api/logs)          Grafana/Loki
─────────────────────────          ─────────────────────────────────
socket Docker Unix                 Volume bind mount partagé
  └─ POST /containers/exec         ./jmeter/logs → /jmeter-logs (rw)
  └─ tail -n N /jmeter-logs/       Promtail lit /jmeter-logs/jmeter.log
       jmeter.log                  → push Loki → panel Grafana

Volume partagé

# perfshop-jmeter
- ./jmeter/logs:/jmeter-logs        # JMeter écrit ici via -j

# perfshop-promtail  
- ./jmeter/logs:/jmeter-logs:ro     # Promtail lit en lecture seule

Commande JMeter — ajout de -j

jmeter -n -t /scenarios/... -j /jmeter-logs/jmeter.log ...

🔧 Détail des implémentations

GET /api/logs — socket Docker Unix (pas de docker-cli)

Deux fonctions internes :

  • dockerExecOutput(container, cmd)POST /exec + POST /exec/{id}/start avec décodage protocole multiplexé Docker (header 8 octets), flag done anti-double-resolve
  • dockerContainerLogs(container)GET /containers/{id}/logs?stdout=1&stderr=1&tail=N
target Source Méthode
jmeter /jmeter-logs/jmeter.log dans le container dockerExecOutput + tail -n N
ui Logs stdout du container perfshop-jmeter-ui dockerContainerLogs
both Les deux Les deux

Promtail — job fichier statique

- job_name: jmeter-log
  static_configs:
    - targets: [localhost]
      labels:
        job: perfshop-jmeter
        container: perfshop-jmeter
        __path__: /jmeter-logs/jmeter.log
Labels container: perfshop-jmeter permettent au panel Grafana d'utiliser la même query {container="perfshop-jmeter"} que les autres containers.

Panneau IHM — "Logs en direct"

  • Sélecteurs target (both/jmeter/ui) et lines (50/80/150/300)
  • Boutons 🔄 Actualiser + ▶ Auto (interval 5s)
  • Deux colonnes <pre> scrollables
  • Hook patchLogsOnStatus : auto ON pendant tir, OFF à la fin
  • Gestion 401 : stop auto silencieux sans reload si écran login visible

🐛 Bugs rencontrés et corrigés

Bug Cause Correction
Unexpected token '<' au lancement d'un tir Session expirée (401) → HTML Synology renvoyé Re-login — pas un bug code
Reload en boucle au clic login logsRefresh initial déclenchait window.location.reload() sur 401 Vérification loginScreen visible avant reload
perfshop-jmeter absent de Loki Containers non recréés avec --force-recreate docker compose up -d --force-recreate
jmeter/logs/ vide après tir server.js non SCP-é → -j absent de la cmd JMeter SCP + docker restart perfshop-jmeter-ui
/api/run retournait HTML 500 Handler async sans try/catch → Express error handler HTML Try/catch ajouté, JSON propre en cas d'erreur

✅ Validation finale

# Loki indexe bien les deux containers
curl -s "http://localhost:3100/loki/api/v1/label/container/values"
# → ["perfshop-app","perfshop-db","perfshop-frontend","perfshop-jmeter","perfshop-jmeter-ui"]

# Fichier créé après premier tir
ls -la /volume4/docker-speed/perfshop/jmeter/logs/jmeter.log
  • ✅ Logs perfshop-jmeter-ui dans Grafana (docker_sd)
  • ✅ Logs perfshop-jmeter dans Grafana (fichier statique Promtail)
  • ✅ Panneau "Logs en direct" dans l'IHM — deux colonnes auto-refresh
  • ✅ Compatible Docker Desktop Windows (WSL2 + socket Docker)
  • ✅ Compatible NAS Synology DSM 7 (sans docker-cli)

🗂️ Bilan qualité

Erreurs Claude : - Modification inutile try/catch api/run diagnostiquée comme cause du HTML 500 → c'était la session expirée. Modification gardée car défensive et correcte. - docker compose up -d --no-deps ne recrée pas les containers sans --force-recreate — diagnostic en 2 étapes.

Syndrome "Fix Théorique Sans Validation Empirique" : 0 occurrence — validation NAS à chaque étape.


🎓 Valeur pédagogique

Concept Ce que l'apprenant voit
Observabilité multi-niveaux Même logs accessibles dans l'IHM et dans Grafana
Socket Docker sans CLI API HTTP Unix socket — fonctionne sur NAS, Docker Desktop, CI
Promtail fichier statique Alternative au docker_sd quand les logs Docker sont vides
Volume partagé multi-containers Pattern producer/consumer via bind mount
Gestion session expirée UX propre — stop auto, pas de boucle reload