fix(logs): Implement ultra-robust path resolution and retry logic in LogPruner

This commit is contained in:
2026-03-07 12:44:25 -05:00
parent 6f8c2c78e8
commit f27b971565

View File

@@ -1,6 +1,7 @@
import os import os
import shutil import shutil
import sys import sys
import time
from datetime import datetime, timedelta from datetime import datetime, timedelta
from src.log_registry import LogRegistry from src.log_registry import LogRegistry
@@ -49,15 +50,31 @@ class LogPruner:
if not session_path: if not session_path:
continue continue
# Robust path resolution # RESOLUTION STRATEGY:
if os.path.isabs(session_path): # 1. Try as-is (absolute or relative to project root)
resolved_path = session_path # 2. Try as a sub-directory of self.logs_dir (e.g. logs/sessions/session_id)
else: # 3. Try relative to parent of logs_dir if it starts with 'logs/'
# Always resolve relative to project root
resolved_path = os.path.abspath(os.path.join(project_root, session_path))
if not os.path.isdir(resolved_path): candidates = []
sys.stderr.write(f"[LogPruner] Skipping {session_id}: directory not found at {resolved_path}\n") if os.path.isabs(session_path):
candidates.append(session_path)
else:
candidates.append(os.path.abspath(os.path.join(project_root, session_path)))
candidates.append(os.path.abspath(os.path.join(self.logs_dir, session_id)))
candidates.append(os.path.abspath(os.path.join(self.logs_dir, os.path.basename(session_path))))
resolved_path = None
for cand in candidates:
if os.path.isdir(cand):
resolved_path = cand
break
if not resolved_path:
# If we can't find it, we still remove it from the registry if it's "empty"
# so it stops cluttering the UI.
sys.stderr.write(f"[LogPruner] Could not find directory for {session_id} in candidates. Removing registry entry.\n")
if session_id in self.log_registry.data:
del self.log_registry.data[session_id]
continue continue
# Calculate total size of files in the directory # Calculate total size of files in the directory
@@ -71,11 +88,26 @@ class LogPruner:
continue continue
# Prune if the total size is less than threshold # Prune if the total size is less than threshold
# (The user requested always removing empty logs, get_old_non_whitelisted_sessions already handles the 'empty' check)
if total_size < (min_size_kb * 1024) or total_size == 0: if total_size < (min_size_kb * 1024) or total_size == 0:
try: try:
sys.stderr.write(f"[LogPruner] Removing {session_id} at {resolved_path} (Size: {total_size} bytes)\n") sys.stderr.write(f"[LogPruner] Removing {session_id} at {resolved_path} (Size: {total_size} bytes)\n")
shutil.rmtree(resolved_path)
# Windows specific: sometimes files are locked.
# We try a few times with small delays.
def remove_readonly(func, path, excinfo):
os.chmod(path, 0o777)
func(path)
for attempt in range(3):
try:
shutil.rmtree(resolved_path, onerror=remove_readonly)
break
except OSError:
if attempt < 2:
time.sleep(0.1)
else:
raise
# Also remove from registry to keep it in sync # Also remove from registry to keep it in sync
if session_id in self.log_registry.data: if session_id in self.log_registry.data:
del self.log_registry.data[session_id] del self.log_registry.data[session_id]