From 04aafae90bf4fbbf3a3659b5fa87c3ee0794f315 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Thu, 27 Nov 2025 03:51:51 -0700 Subject: [PATCH] Fixed Issues with Log Management System --- Data/Engine/services/API/__init__.py | 7 ++- .../services/API/server/log_management.py | 52 ++++++++++++++++++- .../src/Admin/Log_Management.jsx | 1 - 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/Data/Engine/services/API/__init__.py b/Data/Engine/services/API/__init__.py index f449beb8..35a6b810 100644 --- a/Data/Engine/services/API/__init__.py +++ b/Data/Engine/services/API/__init__.py @@ -120,6 +120,8 @@ _QUIET_SERVICE_LOGS = {"scheduled_jobs"} def _make_service_logger(base: Path, logger: logging.Logger) -> Callable[[str, str, Optional[str]], None]: + quiet_services = {name.strip().lower().replace("-", "_") for name in _QUIET_SERVICE_LOGS if name} + def _log(service: str, msg: str, scope: Optional[str] = None, *, level: str = "INFO") -> None: level_upper = level.upper() service_key = (service or "").strip().lower() @@ -134,12 +136,13 @@ def _make_service_logger(base: Path, logger: logging.Logger) -> Callable[[str, s prefix_parts.append(f"[CONTEXT-{resolved_scope}]") prefix = "".join(prefix_parts) with path.open("a", encoding="utf-8") as handle: - handle.write(f"[{timestamp}] {prefix} {msg}\\n") + handle.write(f"[{timestamp}] {prefix} {msg}\n") except Exception: logger.debug("Failed to write service log entry", exc_info=True) numeric_level = getattr(logging, level_upper, logging.INFO) - if service_key not in _QUIET_SERVICE_LOGS: + suppress_engine_log = service_key in quiet_services or service_key.replace("-", "_") in quiet_services + if not suppress_engine_log: logger.log(numeric_level, "[service:%s] %s", service, msg) return _log diff --git a/Data/Engine/services/API/server/log_management.py b/Data/Engine/services/API/server/log_management.py index 34594548..b390daa6 100644 --- a/Data/Engine/services/API/server/log_management.py +++ b/Data/Engine/services/API/server/log_management.py @@ -203,6 +203,46 @@ class EngineLogManager: files.append(entry) return files + def _truncate_active_log(self, path: Path) -> bool: + """Best-effort truncate for logs held open by logging handlers (Windows-safe).""" + + resolved = path.resolve() + truncated = False + for logger_name in list(logging.root.manager.loggerDict.keys()) + [self.logger.name]: + try: + logger_obj = logging.getLogger(logger_name) + except Exception: + continue + handlers = list(getattr(logger_obj, "handlers", [])) + for handler in handlers: + base = getattr(handler, "baseFilename", None) + if not base: + continue + try: + if Path(base).resolve() != resolved: + continue + except Exception: + continue + try: + handler.acquire() + stream = getattr(handler, "stream", None) + if stream: + stream.seek(0) + stream.truncate() + stream.flush() + else: + with resolved.open("w", encoding="utf-8"): + pass + truncated = True + except Exception: + self.logger.debug("Failed to truncate active log file: %s", path, exc_info=True) + finally: + try: + handler.release() + except Exception: + pass + return truncated + def apply_retention(self, retention: Mapping[str, int], default_days: int) -> List[str]: deleted: List[str] = [] now = datetime.now(tz=timezone.utc) @@ -306,7 +346,11 @@ class EngineLogManager: def delete_file(self, filename: str) -> str: path = self._resolve(filename) - path.unlink() + try: + path.unlink() + except PermissionError: + if not self._truncate_active_log(path): + raise return filename def delete_family(self, filename: str) -> List[str]: @@ -320,6 +364,12 @@ class EngineLogManager: entry.unlink() except FileNotFoundError: continue + except PermissionError: + handled = self._truncate_active_log(entry) + if handled: + deleted.append(entry.name) + else: + self.logger.debug("Failed to delete family log file (in use): %s", entry, exc_info=True) except Exception: self.logger.debug("Failed to delete family log file: %s", entry, exc_info=True) else: diff --git a/Data/Engine/web-interface/src/Admin/Log_Management.jsx b/Data/Engine/web-interface/src/Admin/Log_Management.jsx index decd3969..77bc82f0 100644 --- a/Data/Engine/web-interface/src/Admin/Log_Management.jsx +++ b/Data/Engine/web-interface/src/Admin/Log_Management.jsx @@ -322,7 +322,6 @@ const defaultColDef = useMemo( setLogs(Array.isArray(data?.logs) ? data.logs : logs); setEntries([]); setEntriesMeta(null); - setActionMessage("Log files deleted."); if (target) { sendNotification(`Log "${target}" Deleted Successfully`); }