mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-16 02:05:48 -07:00
Fixed Issues with Log Management System
This commit is contained in:
@@ -120,6 +120,8 @@ _QUIET_SERVICE_LOGS = {"scheduled_jobs"}
|
|||||||
|
|
||||||
|
|
||||||
def _make_service_logger(base: Path, logger: logging.Logger) -> Callable[[str, str, Optional[str]], None]:
|
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:
|
def _log(service: str, msg: str, scope: Optional[str] = None, *, level: str = "INFO") -> None:
|
||||||
level_upper = level.upper()
|
level_upper = level.upper()
|
||||||
service_key = (service or "").strip().lower()
|
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_parts.append(f"[CONTEXT-{resolved_scope}]")
|
||||||
prefix = "".join(prefix_parts)
|
prefix = "".join(prefix_parts)
|
||||||
with path.open("a", encoding="utf-8") as handle:
|
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:
|
except Exception:
|
||||||
logger.debug("Failed to write service log entry", exc_info=True)
|
logger.debug("Failed to write service log entry", exc_info=True)
|
||||||
|
|
||||||
numeric_level = getattr(logging, level_upper, logging.INFO)
|
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)
|
logger.log(numeric_level, "[service:%s] %s", service, msg)
|
||||||
|
|
||||||
return _log
|
return _log
|
||||||
|
|||||||
@@ -203,6 +203,46 @@ class EngineLogManager:
|
|||||||
files.append(entry)
|
files.append(entry)
|
||||||
return files
|
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]:
|
def apply_retention(self, retention: Mapping[str, int], default_days: int) -> List[str]:
|
||||||
deleted: List[str] = []
|
deleted: List[str] = []
|
||||||
now = datetime.now(tz=timezone.utc)
|
now = datetime.now(tz=timezone.utc)
|
||||||
@@ -306,7 +346,11 @@ class EngineLogManager:
|
|||||||
|
|
||||||
def delete_file(self, filename: str) -> str:
|
def delete_file(self, filename: str) -> str:
|
||||||
path = self._resolve(filename)
|
path = self._resolve(filename)
|
||||||
path.unlink()
|
try:
|
||||||
|
path.unlink()
|
||||||
|
except PermissionError:
|
||||||
|
if not self._truncate_active_log(path):
|
||||||
|
raise
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
def delete_family(self, filename: str) -> List[str]:
|
def delete_family(self, filename: str) -> List[str]:
|
||||||
@@ -320,6 +364,12 @@ class EngineLogManager:
|
|||||||
entry.unlink()
|
entry.unlink()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
continue
|
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:
|
except Exception:
|
||||||
self.logger.debug("Failed to delete family log file: %s", entry, exc_info=True)
|
self.logger.debug("Failed to delete family log file: %s", entry, exc_info=True)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -322,7 +322,6 @@ const defaultColDef = useMemo(
|
|||||||
setLogs(Array.isArray(data?.logs) ? data.logs : logs);
|
setLogs(Array.isArray(data?.logs) ? data.logs : logs);
|
||||||
setEntries([]);
|
setEntries([]);
|
||||||
setEntriesMeta(null);
|
setEntriesMeta(null);
|
||||||
setActionMessage("Log files deleted.");
|
|
||||||
if (target) {
|
if (target) {
|
||||||
sendNotification(`Log "${target}" Deleted Successfully`);
|
sendNotification(`Log "${target}" Deleted Successfully`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user