From de1a071c297a1db40627b9288a8411d50e0c9b70 Mon Sep 17 00:00:00 2001 From: sylvain Date: Thu, 2 Apr 2026 09:10:38 +0000 Subject: [PATCH] fix: pas de favoritisme pour la machine locale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Retire _collect_local_logs() du démarrage du slot d'analyse - La machine locale passe par le même pipeline MQTT que les distantes - Nouveau job APScheduler indépendant: local_collect_time (config DB) - Commande: logwatch schedule local HH:MM / off Co-Authored-By: Claude Sonnet 4.6 --- agent_logwatch.py | 20 ++++++++++++++---- config/system_prompt.txt | 2 ++ data/logwatch.db | Bin 40960 -> 40960 bytes skills/__pycache__/logwatch.cpython-313.pyc | Bin 12932 -> 13955 bytes skills/logwatch.py | 22 +++++++++++++++++++- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/agent_logwatch.py b/agent_logwatch.py index 6ccf8a1..9bb3c69 100644 --- a/agent_logwatch.py +++ b/agent_logwatch.py @@ -154,6 +154,7 @@ class LogWatchAgent(BaseAgent): INSERT OR IGNORE INTO agent_config VALUES ('max_overage_minutes', '30'); INSERT OR IGNORE INTO agent_config VALUES ('enabled', '1'); INSERT OR IGNORE INTO agent_config VALUES ('log_retention_days', '7'); + INSERT OR IGNORE INTO agent_config VALUES ('local_collect_time', ''); """) def _cfg(self, key: str, default: str = '') -> str: @@ -288,7 +289,7 @@ class LogWatchAgent(BaseAgent): """(Re)programme les jobs APScheduler selon la config DB.""" if not self._scheduler: return - for job_id in ('_slot_start', '_slot_end'): + for job_id in ('_slot_start', '_slot_end', '_local_collect'): try: self._scheduler.remove_job(job_id) except Exception: @@ -313,6 +314,20 @@ class LogWatchAgent(BaseAgent): self._scheduler.add_job( self._signal_slot_end, 'cron', hour=eh, minute=em, id='_slot_end' ) + + # Job de collecte locale (séparé, configurable indépendamment) + local_collect = self._cfg('local_collect_time', '') + if local_collect: + try: + lh, lm = map(int, local_collect.split(':')) + self._scheduler.add_job( + self._collect_local_logs, 'cron', + hour=lh, minute=lm, id='_local_collect' + ) + logger.info(f"Collecte locale programmée: {local_collect}") + except ValueError: + logger.error(f"Format local_collect_time invalide: {local_collect}") + logger.info(f"Analyse programmée: {start_str} → {end_str}") def _start_slot(self): @@ -324,9 +339,6 @@ class LogWatchAgent(BaseAgent): if self._slot_end_time <= now: self._slot_end_time += timedelta(days=1) - # Collecter les logs locaux avant de commencer l'analyse - self._collect_local_logs() - self._analysis_stop.clear() self._analysis_thread = threading.Thread( target=self._analysis_loop, daemon=True, name="logwatch-analysis" diff --git a/config/system_prompt.txt b/config/system_prompt.txt index a82015e..99fcc30 100644 --- a/config/system_prompt.txt +++ b/config/system_prompt.txt @@ -19,6 +19,8 @@ Tu reçois des instructions via MQTT (depuis Nexus) ou XMPP (directement). - `schedule show` : voir le créneau horaire configuré - `schedule set HH:MM-HH:MM` : définir le créneau d'analyse automatique - `schedule enable/disable` : activer/désactiver l'analyse automatique + - `schedule local HH:MM` : heure de collecte des logs locaux (machine hébergeant l'agent) + - `schedule local off` : désactiver la collecte locale automatique - `overage ` : définir le dépassement maximum autorisé - `retention ` : durée de conservation des logs filtrés - `analyze ` : lancer l'analyse d'une machine spécifique maintenant diff --git a/data/logwatch.db b/data/logwatch.db index 99064ed57453f5b1e60cfa247d44d45670deb473..087a83c3be088ee43960972fd3b915e435d7e076 100644 GIT binary patch delta 115 zcmZoTz|?SnX@WE(??f4AM&6AHOX>yK_-h#WU+`bz-@(6{e=>i~W#=r)X9_k3whZ1yBYZJ^1tKX$=|)1Z$dS{xEQk`Bc@`u$&2R4 F0RURNCZhlV delta 66 zcmV-I0KNZ!zyg540+1U443Qi|0SvKVq<;M28Iz))Uf%#0VgLXD diff --git a/skills/__pycache__/logwatch.cpython-313.pyc b/skills/__pycache__/logwatch.cpython-313.pyc index feada4a4996e736e3fe481104e2906da90f015f4..9b4ddba960f98043df047c0d87cc658828563223 100644 GIT binary patch delta 2975 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 str: f"État : {'activé ✅' if enabled else 'désactivé ❌'}" ) + if sub == 'local': + # schedule local HH:MM — configurer l'heure de collecte locale + # schedule local off — désactiver + if not sub_rest: + val = _cfg(context, 'local_collect_time', '') + return f"Collecte locale : {val or 'désactivée'}" + if sub_rest.lower() == 'off': + _set_cfg(context, 'local_collect_time', '') + context.agent._reload_schedule() + return "✅ Collecte locale désactivée." + try: + lh, lm = map(int, sub_rest.split(':')) + if not (0 <= lh < 24 and 0 <= lm < 60): + return "Heure invalide." + except ValueError: + return "Format: schedule local HH:MM (ex: 01:00) ou off" + _set_cfg(context, 'local_collect_time', sub_rest.strip()) + context.agent._reload_schedule() + return f"✅ Collecte locale programmée à {sub_rest.strip()}." + if sub == 'set': # Format : HH:MM-HH:MM if '-' not in sub_rest: @@ -140,7 +160,7 @@ def run(args: str, context) -> str: context.agent._reload_schedule() return f"✅ Analyse automatique {'activée' if val=='1' else 'désactivée'}." - return "Sub-commande inconnue. Utilise : show, set , enable, disable" + return "Sub-commande inconnue. Utilise : show, set , enable, disable, local " # ── overage ─────────────────────────────────────────────────────────────── if action == 'overage':