21 — Logging et débogage
Déboguer un système multi-agent est plus difficile qu’un programme classique : plusieurs processus s’exécutent en parallèle, les messages circulent de façon asynchrone, et un problème peut venir d’un timing, d’un message perdu, ou d’un état inattendu. gAgent fournit plusieurs outils pour comprendre ce qui se passe.
Les deux modes de log
gAgent dispose de deux systèmes de logs indépendants :
Mode |
Activation |
Format |
|---|---|---|
Texte |
Toujours actif (stdout) |
|
JSON Lines |
Variable |
Un objet JSON par ligne (structuré, filtrable) |
Le mode JSON Lines est celui à utiliser pour analyser sérieusement un problème. Le mode texte suffit pour les logs en cours de développement.
Activer les logs JSON
GAGENT_LOG=gagent.jsonl ./build/ma_simulation
Chaque message ACL envoyé ou reçu, chaque démarrage/arrêt d’agent,
chaque transition d’état est automatiquement enregistré. Si
GAGENT_LOG n’est pas défini, aucun fichier n’est créé (pas
d’overhead en production).
Ce qui est enregistré automatiquement
Événement |
Contenu |
|---|---|
|
Nom de l’agent + PID au démarrage |
|
Nom de l’agent + PID à l’arrêt |
|
Transitions d’état : |
|
Expéditeur, destinataire, performative, conversation_id, contenu (120 car. max) |
|
Idem, à la réception |
Exemple de log JSON :
{"ts":"2026-03-24T14:32:05.001Z","event":"agent_start","agent":"coordinateur","pid":"12347"}
{"ts":"2026-03-24T14:32:05.012Z","event":"acl_send","from":"coordinateur","to":"transporteur-a","perf":"cfp","conv":"cnp-coord-75727","content":"livraison Paris→Lyon"}
{"ts":"2026-03-24T14:32:05.045Z","event":"acl_recv","to":"transporteur-a","from":"coordinateur","perf":"cfp","conv":"cnp-coord-75727","content":"livraison Paris→Lyon"}
{"ts":"2026-03-24T14:32:05.089Z","event":"acl_send","from":"transporteur-a","to":"coordinateur","perf":"propose","conv":"cnp-coord-75727","content":"95"}
Ajouter ses propres événements
Vous pouvez émettre vos propres lignes JSON depuis n’importe quel agent ou behaviour :
#include <gagent/utils/Logger.hpp>
// Macro (recommandée)
LOG_JSON("decision",
{"agent", "coordinateur"},
{"gagnant", meilleur_candidat},
{"prix", std::to_string(meilleur_prix)}
);
// Log texte classique
LOG_INFO("décision prise : " + meilleur_candidat);
LOG_WARNING("aucune proposition reçue");
Les niveaux disponibles : LOG_DEBUG, LOG_INFO, LOG_WARNING,
LOG_ERROR, LOG_CRITICAL.
Analyser les logs avec jq
jq est l’outil de référence pour interroger des fichiers JSON Lines.
Suivre les messages en temps réel :
tail -f gagent.jsonl | jq .
Voir tous les messages d’une conversation :
jq 'select(.conv == "cnp-coord-75727")' gagent.jsonl
Reconstituer la chronologie d’un agent :
jq 'select(.agent == "coordinateur" or .from == "coordinateur" or .to == "coordinateur")' gagent.jsonl
Compter les messages par performative :
jq -r '.perf // empty' gagent.jsonl | sort | uniq -c | sort -rn
8 inform
4 cfp
4 propose
2 accept-proposal
1 reject-proposal
Détecter les timeouts (messages envoyés sans réponse) :
# Lister les CFP sans PROPOSE correspondant
jq 'select(.event=="acl_send" and .perf=="cfp") | .conv' gagent.jsonl
Puis vérifier qu’il existe bien un propose pour chaque conv.
Stratégies de débogage courantes
Problème : un agent ne reçoit jamais de message
Vérifiez que l’agent appelle
acl_bind("nom")danssetup()avant toutacl_receive().Vérifiez dans les logs que
acl_sendvers ce nom a bien été émis.Vérifiez que le nom dans
acl_send("nom", ...)est identique au nom dansacl_bind("nom")etacl_receive("nom", ...).
Problème : les messages arrivent dans le mauvais ordre
Les agents sont des processus parallèles — l’ordre d’exécution n’est
pas garanti. Utilisez les conversation_id pour corréler les
échanges, et les timeouts pour gérer les cas où un message est en retard.
Problème : un agent se bloque
# Voir l'état de tous les agents
./bin/agentmanager watch
Si un agent est en état waiting depuis longtemps, il est probablement
bloqué sur un acl_receive() sans message entrant. Vérifiez :
Que l’expéditeur a bien envoyé son message (
acl_senddans les logs).Que le timeout de
acl_receive()est suffisamment long.Que les noms d’agents correspondent des deux côtés.
Problème : messages perdus au démarrage
C’est le problème du slow joiner : un agent envoie un message avant
que le destinataire ait eu le temps de binder son socket. Solution :
appelez acl_bind("nom") en tout début de setup(), avant même
d’ajouter des behaviours.
void setup() override {
acl_bind("mon-agent"); // ← en premier
addBehaviour(new MonBehaviour(this));
}
Utiliser agentmanager en complément
Pendant l’exécution, agentmanager watch vous donne une vue en temps
réel des agents et de leur état. Combinez-le avec les logs JSON pour
une vision complète :
# Terminal 1 — logs en temps réel
tail -f gagent.jsonl | jq 'select(.event | startswith("acl"))'
# Terminal 2 — état des agents
./bin/agentmanager watch 500
# Terminal 3 — votre simulation
GAGENT_LOG=gagent.jsonl ./build/ma_simulation