""" Logger & Audit Module ====================== R4: Tất cả thao tác phải được log. - app.log: log chung (rotate hàng ngày) - audit.log: vi phạm rule - performance.log: thời gian xử lý """ import logging import os import time import functools from datetime import datetime from logging.handlers import TimedRotatingFileHandler LOG_DIR = os.path.join(os.path.dirname(__file__), 'logs') os.makedirs(LOG_DIR, exist_ok=True) def _make_logger(name, filename, level=logging.INFO): logger = logging.getLogger(name) if logger.handlers: return logger logger.setLevel(level) fh = TimedRotatingFileHandler( os.path.join(LOG_DIR, filename), when='midnight', interval=1, backupCount=30, encoding='utf-8' ) fh.suffix = "%Y-%m-%d" fmt = logging.Formatter('%(asctime)s | %(levelname)-7s | %(message)s', '%Y-%m-%d %H:%M:%S') fh.setFormatter(fmt) logger.addHandler(fh) # Also console ch = logging.StreamHandler() ch.setFormatter(fmt) ch.setLevel(logging.WARNING) logger.addHandler(ch) return logger # === Three separate loggers === app_log = _make_logger('app', 'app.log') audit_log = _make_logger('audit', 'audit.log', logging.WARNING) perf_log = _make_logger('perf', 'performance.log') def log_action(action, detail="", level="info"): """Log an application action.""" msg = f"[{action}] {detail}" getattr(app_log, level, app_log.info)(msg) def log_audit(rule, message, severity="WARNING"): """Log a rule violation or audit event.""" audit_log.warning(f"[RULE {rule}] {message}") def log_performance(action, duration_ms, threshold_ms=None): """Log performance measurement.""" status = "OK" if threshold_ms and duration_ms > threshold_ms: status = "SLOW" audit_log.warning(f"[RULE R5] {action} took {duration_ms:.0f}ms > {threshold_ms}ms threshold") perf_log.info(f"[{status}] {action}: {duration_ms:.1f}ms") def timed(action_name, threshold_ms=None): """Decorator to measure and log function execution time.""" def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() try: result = func(*args, **kwargs) elapsed = (time.perf_counter() - start) * 1000 log_performance(action_name, elapsed, threshold_ms) return result except Exception as e: elapsed = (time.perf_counter() - start) * 1000 log_performance(f"{action_name} [ERROR]", elapsed, threshold_ms) raise return wrapper return decorator class Timer: """Context manager for timing blocks of code.""" def __init__(self, name, threshold_ms=None): self.name = name self.threshold_ms = threshold_ms self.elapsed_ms = 0 def __enter__(self): self.start = time.perf_counter() return self def __exit__(self, *args): self.elapsed_ms = (time.perf_counter() - self.start) * 1000 log_performance(self.name, self.elapsed_ms, self.threshold_ms)