feat: stockage rapports DB + filtres affinés

- Table 'reports' : stockage des rapports LLM (machine, date, contenu, nb erreurs)
- logwatch report [hostname] [date] : relire un rapport stocké
- Filtres refactorisés : tier 1 (uppercase exacts) + tier 2 (contextuels précis)
- EXCLUDE_PATTERNS : exclure le bruit connu (Started, LogWatch lui-même...)
- Déduplication : max 5 occurrences de la même ligne par collecte
- Résultat : 0.7% de rétention vs 33% avant

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-02 09:42:25 +00:00
parent 7c5b270144
commit d496e1d188
8 changed files with 147 additions and 19 deletions
+49
View File
@@ -26,6 +26,7 @@ USAGE = (
"SKILL:logwatch ARGS:overage <minutes>\n"
"SKILL:logwatch ARGS:analyze <hostname>\n"
"SKILL:logwatch ARGS:analyze_all\n"
"SKILL:logwatch ARGS:report [hostname] [YYYY-MM-DD]\n"
"SKILL:logwatch ARGS:collect [since]\n"
"SKILL:logwatch ARGS:retention <jours>\n"
"SKILL:logwatch ARGS:logs <hostname> [N]\n"
@@ -260,6 +261,54 @@ def run(args: str, context) -> str:
)
return "\n".join(lines)
# ── report <hostname> [date] ──────────────────────────────────────────────
if action == 'report':
p = rest.split(None, 1)
hostname = p[0].strip() if p else ''
date_str = p[1].strip() if len(p) > 1 else ''
if not hostname:
# Sans hostname : liste les derniers rapports toutes machines
with _db(context) as conn:
rows = conn.execute(
"SELECT m.hostname, r.report_date, r.logs_count, r.created_at "
"FROM reports r JOIN machines m ON m.id=r.machine_id "
"ORDER BY r.id DESC LIMIT 20"
).fetchall()
if not rows:
return "Aucun rapport stocké."
lines = ["── Rapports disponibles ──────────────────────"]
for r in rows:
lines.append(
f" {r['report_date']} | {r['hostname']:<30s} | {r['logs_count']} erreurs"
)
lines.append("\nUtilise : logwatch report <hostname> [YYYY-MM-DD]")
return "\n".join(lines)
with _db(context) as conn:
m = conn.execute(
"SELECT id FROM machines WHERE hostname=?", (hostname,)
).fetchone()
if not m:
return f"Machine '{hostname}' introuvable."
if date_str:
row = conn.execute(
"SELECT content, report_date, logs_count FROM reports "
"WHERE machine_id=? AND report_date=? ORDER BY id DESC LIMIT 1",
(m['id'], date_str)
).fetchone()
else:
row = conn.execute(
"SELECT content, report_date, logs_count FROM reports "
"WHERE machine_id=? ORDER BY id DESC LIMIT 1",
(m['id'],)
).fetchone()
if not row:
return f"Aucun rapport trouvé pour '{hostname}'" + (f" le {date_str}" if date_str else "") + "."
return f"[{row['report_date']}{row['logs_count']} erreurs]\n\n{row['content']}"
# ── collect [since] ───────────────────────────────────────────────────────
if action == 'collect':
since = rest.strip() or 'yesterday'