""" ROTATING STDOUT - Dual logs: 5000 lines (main) + 2500 lines (compact) """ import sys import os from datetime import datetime MAX_LINES = 5000 MAX_LINES_COMPACT = 2500 LOG_FILE = os.environ.get('TRAINING_LOG', '/root/training.log') LOG_FILE_COMPACT = LOG_FILE.replace('.log', '_compact.log') ARCHIVE_DIR = LOG_FILE.replace('.log', '_archives') class RotatingStdout: def __init__(self, max_lines=MAX_LINES, max_compact=MAX_LINES_COMPACT): self.max_lines = max_lines self.max_compact = max_compact self.line_count = 0 self.line_count_compact = 0 self.original_stdout = sys.stdout self.rotating = False os.makedirs(ARCHIVE_DIR, exist_ok=True) self.log_file = open(LOG_FILE, 'a', buffering=1) self.log_compact = open(LOG_FILE_COMPACT, 'a', buffering=1) def _rotate_main(self): self.log_file.close() timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') archive_path = os.path.join(ARCHIVE_DIR, f'archive_{timestamp}.log') with open(LOG_FILE, 'r') as f: lines = f.readlines() keep = min(self.max_lines // 2, len(lines)) with open(archive_path, 'w') as f: f.writelines(lines[:-keep] if keep else lines) with open(LOG_FILE, 'w') as f: f.writelines(lines[-keep:] if keep else []) self.line_count = keep self.log_file = open(LOG_FILE, 'a', buffering=1) def _rotate_compact(self): self.log_compact.close() with open(LOG_FILE_COMPACT, 'r') as f: lines = f.readlines() keep = min(self.max_compact // 2, len(lines)) with open(LOG_FILE_COMPACT, 'w') as f: f.writelines(lines[-keep:] if keep else []) self.line_count_compact = keep self.log_compact = open(LOG_FILE_COMPACT, 'a', buffering=1) def _rotate_if_needed(self): if self.rotating: return self.rotating = True if self.line_count > self.max_lines: self._rotate_main() if self.line_count_compact > self.max_compact: self._rotate_compact() self.rotating = False def write(self, text): self.original_stdout.write(text) if not self.rotating: self.log_file.write(text) self.log_compact.write(text) newlines = text.count('\n') self.line_count += newlines self.line_count_compact += newlines self._rotate_if_needed() def flush(self): self.original_stdout.flush() if self.log_file: self.log_file.flush() if self.log_compact: self.log_compact.flush() def install_rotating_log(): rotating = RotatingStdout() sys.stdout = rotating sys.stderr = rotating print(f"[rotating_log] Active. Main={MAX_LINES}, Compact={MAX_LINES_COMPACT} lines.")