This commit is contained in:
Dobromir Popov
2025-07-30 11:40:30 +03:00
parent 6ca19f4536
commit 36f429a0e2
6 changed files with 528 additions and 211 deletions

View File

@ -38,21 +38,15 @@ class SafeFormatter(logging.Formatter):
class SafeStreamHandler(logging.StreamHandler):
"""Stream handler that forces UTF-8 encoding where supported"""
def __init__(self, stream=None):
super().__init__(stream)
# Try to set UTF-8 encoding on stdout/stderr if supported
if hasattr(self.stream, 'reconfigure'):
try:
if platform.system() == "Windows":
# On Windows, use errors='ignore'
self.stream.reconfigure(encoding='utf-8', errors='ignore')
else:
# On Unix-like systems, use backslashreplace
self.stream.reconfigure(encoding='utf-8', errors='backslashreplace')
except (AttributeError, OSError):
# If reconfigure is not available or fails, continue silently
pass
if platform.system() == "Windows":
# Force UTF-8 encoding on Windows
if hasattr(stream, 'reconfigure'):
try:
stream.reconfigure(encoding='utf-8', errors='ignore')
except:
pass
def setup_safe_logging(log_level=logging.INFO, log_file='logs/safe_logging.log'):
"""Setup logging with SafeFormatter and UTF-8 encoding with enhanced persistence
@ -165,3 +159,69 @@ def setup_safe_logging(log_level=logging.INFO, log_file='logs/safe_logging.log')
# Register atexit handler for normal shutdown
atexit.register(flush_all_logs)
def setup_training_logger(log_level=logging.INFO, log_file='logs/training.log'):
"""Setup a separate training logger that writes to training.log
Args:
log_level: Logging level (default: INFO)
log_file: Path to training log file (default: logs/training.log)
Returns:
logging.Logger: The training logger instance
"""
# Ensure logs directory exists
log_path = Path(log_file)
log_path.parent.mkdir(parents=True, exist_ok=True)
# Create training logger
training_logger = logging.getLogger('training')
training_logger.setLevel(log_level)
# Clear existing handlers to avoid duplicates
for handler in training_logger.handlers[:]:
training_logger.removeHandler(handler)
# Create file handler for training logs
try:
encoding_kwargs = {
"encoding": "utf-8",
"errors": "ignore" if platform.system() == "Windows" else "backslashreplace"
}
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler(
log_file,
maxBytes=10*1024*1024, # 10MB max file size
backupCount=5, # Keep 5 backup files
**encoding_kwargs
)
file_handler.setFormatter(SafeFormatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
# Force immediate flush for training logs
class FlushingHandler(RotatingFileHandler):
def emit(self, record):
super().emit(record)
self.flush() # Force flush after each log
file_handler = FlushingHandler(
log_file,
maxBytes=10*1024*1024,
backupCount=5,
**encoding_kwargs
)
file_handler.setFormatter(SafeFormatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
))
training_logger.addHandler(file_handler)
except (OSError, IOError) as e:
print(f"Warning: Could not create training log file {log_file}: {e}", file=sys.stderr)
# Prevent propagation to root logger to avoid duplicate logs
training_logger.propagate = False
return training_logger