"""Logging et journalisation des décisions IA. Fournit log_info() et log_decision() utilisés par tous les modules. """ import os import json import logging from datetime import datetime from logging.handlers import RotatingFileHandler from .config import ( LOG_FILE, LOG_BACKUP_COUNT, TRAINING_HISTORY_FILE, CONTAMINATION, ANOMALY_THRESHOLD, MODEL_DIR, ) # ─── Initialisation des répertoires ───────────────────────────────────────── os.makedirs(os.path.dirname(LOG_FILE), exist_ok=True) os.makedirs(MODEL_DIR, exist_ok=True) # ─── Logger principal ─────────────────────────────────────────────────────── logger = logging.getLogger('bot_detector') logger.setLevel(logging.DEBUG) _console_handler = logging.StreamHandler() _console_handler.setFormatter(logging.Formatter('[%(asctime)s] %(message)s', '%Y-%m-%d %H:%M:%S')) logger.addHandler(_console_handler) _file_handler = RotatingFileHandler( LOG_FILE, maxBytes=50 * 1024 * 1024, backupCount=LOG_BACKUP_COUNT, encoding='utf-8' ) _file_handler.setFormatter(logging.Formatter('%(message)s')) logger.addHandler(_file_handler) def log_info(message: str): """Enregistre un message de niveau INFO dans le logger du service.""" logger.info(message) def log_decision(event: str, cycle_id: str, model: str = '', row: dict = None): """Enregistre un événement de décision IA au format JSONL dans le fichier de log rotatif. Chaque ligne contient l'horodatage, le cycle_id, l'événement, le modèle, la contamination, le seuil et les données supplémentaires de ``row``. """ entry = { 'ts': datetime.now().strftime('%Y-%m-%dT%H:%M:%S'), 'cycle_id': cycle_id, 'event': event, 'model': model, 'contamination': CONTAMINATION, 'threshold': ANOMALY_THRESHOLD, } if row: entry.update(row) _file_handler.stream.write(json.dumps(entry, ensure_ascii=False, default=str) + '\n') _file_handler.stream.flush() def append_training_history(entry: dict): """Ajoute une entrée de métadonnées d'entraînement au fichier d'historique JSONL.""" with open(TRAINING_HISTORY_FILE, 'a', encoding='utf-8') as f: f.write(json.dumps(entry, ensure_ascii=False, default=str) + '\n')