| """ |
| 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.") |
|
|