mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Moved sys.path modification from canvas_node.py to __init__.py for better package management. Improved logger formatting for colored output and enhanced file logging configuration with error handling for log directory creation. Added python/__init__.py to make the 'python' directory a package.
321 lines
11 KiB
Python
321 lines
11 KiB
Python
"""
|
|
Logger - Centralny system logowania dla ComfyUI-LayerForge (Python)
|
|
|
|
Funkcje:
|
|
- Różne poziomy logowania (DEBUG, INFO, WARN, ERROR)
|
|
- Możliwość włączania/wyłączania logów globalnie lub per moduł
|
|
- Kolorowe logi w konsoli
|
|
- Rotacja plików logów
|
|
- Konfiguracja przez zmienne środowiskowe
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import logging
|
|
import datetime
|
|
from enum import IntEnum
|
|
from logging.handlers import RotatingFileHandler
|
|
import traceback
|
|
|
|
# Poziomy logowania
|
|
class LogLevel(IntEnum):
|
|
DEBUG = 10
|
|
INFO = 20
|
|
WARN = 30
|
|
ERROR = 40
|
|
NONE = 100
|
|
|
|
# Mapowanie poziomów logowania
|
|
LEVEL_MAP = {
|
|
LogLevel.DEBUG: logging.DEBUG,
|
|
LogLevel.INFO: logging.INFO,
|
|
LogLevel.WARN: logging.WARNING,
|
|
LogLevel.ERROR: logging.ERROR,
|
|
LogLevel.NONE: logging.CRITICAL + 1
|
|
}
|
|
|
|
# Kolory ANSI dla różnych poziomów logowania
|
|
COLORS = {
|
|
LogLevel.DEBUG: '\033[90m', # Szary
|
|
LogLevel.INFO: '\033[94m', # Niebieski
|
|
LogLevel.WARN: '\033[93m', # Żółty
|
|
LogLevel.ERROR: '\033[91m', # Czerwony
|
|
'RESET': '\033[0m' # Reset
|
|
}
|
|
|
|
# Konfiguracja domyślna
|
|
DEFAULT_CONFIG = {
|
|
'global_level': LogLevel.INFO,
|
|
'module_settings': {},
|
|
'use_colors': True,
|
|
'log_to_file': False,
|
|
'log_dir': 'logs',
|
|
'max_file_size_mb': 10,
|
|
'backup_count': 5,
|
|
'timestamp_format': '%H:%M:%S',
|
|
}
|
|
|
|
class ColoredFormatter(logging.Formatter):
|
|
"""Formatter dodający kolory do logów w konsoli"""
|
|
|
|
def __init__(self, fmt=None, datefmt=None, use_colors=True):
|
|
super().__init__(fmt, datefmt)
|
|
self.use_colors = use_colors
|
|
|
|
def format(self, record):
|
|
# Get the formatted message from the record
|
|
message = record.getMessage()
|
|
if record.exc_info:
|
|
message += "\n" + self.formatException(record.exc_info)
|
|
|
|
levelname = record.levelname
|
|
|
|
# Build the log prefix
|
|
prefix = '[{}] [{}] [{}]'.format(
|
|
self.formatTime(record, self.datefmt),
|
|
record.name,
|
|
record.levelname
|
|
)
|
|
|
|
# Apply color and bold styling to the prefix
|
|
if self.use_colors and hasattr(LogLevel, levelname):
|
|
level_enum = getattr(LogLevel, levelname)
|
|
if level_enum in COLORS:
|
|
# Apply bold (\033[1m) and color, then reset
|
|
prefix = f"\033[1m{COLORS[level_enum]}{prefix}{COLORS['RESET']}"
|
|
|
|
return f"{prefix} {message}"
|
|
|
|
class LayerForgeLogger:
|
|
"""Główna klasa loggera dla LayerForge"""
|
|
|
|
_instance = None
|
|
|
|
def __new__(cls):
|
|
if cls._instance is None:
|
|
cls._instance = super(LayerForgeLogger, cls).__new__(cls)
|
|
cls._instance._initialized = False
|
|
return cls._instance
|
|
|
|
def __init__(self):
|
|
if self._initialized:
|
|
return
|
|
|
|
self.config = DEFAULT_CONFIG.copy()
|
|
self.enabled = True
|
|
self.loggers = {}
|
|
|
|
# Załaduj konfigurację ze zmiennych środowiskowych
|
|
self._load_config_from_env()
|
|
|
|
self._initialized = True
|
|
|
|
def _load_config_from_env(self):
|
|
"""Załaduj konfigurację ze zmiennych środowiskowych"""
|
|
|
|
# Poziom globalny
|
|
if 'LAYERFORGE_LOG_LEVEL' in os.environ:
|
|
level_name = os.environ['LAYERFORGE_LOG_LEVEL'].upper()
|
|
if hasattr(LogLevel, level_name):
|
|
self.config['global_level'] = getattr(LogLevel, level_name)
|
|
|
|
# Ustawienia modułów
|
|
if 'LAYERFORGE_MODULE_LEVELS' in os.environ:
|
|
try:
|
|
module_settings = json.loads(os.environ['LAYERFORGE_MODULE_LEVELS'])
|
|
for module, level_name in module_settings.items():
|
|
if hasattr(LogLevel, level_name.upper()):
|
|
self.config['module_settings'][module] = getattr(LogLevel, level_name.upper())
|
|
except json.JSONDecodeError:
|
|
pass
|
|
|
|
# Inne ustawienia
|
|
if 'LAYERFORGE_USE_COLORS' in os.environ:
|
|
self.config['use_colors'] = os.environ['LAYERFORGE_USE_COLORS'].lower() == 'true'
|
|
|
|
if 'LAYERFORGE_LOG_TO_FILE' in os.environ:
|
|
self.config['log_to_file'] = os.environ['LAYERFORGE_LOG_TO_FILE'].lower() == 'true'
|
|
|
|
if 'LAYERFORGE_LOG_DIR' in os.environ:
|
|
self.config['log_dir'] = os.environ['LAYERFORGE_LOG_DIR']
|
|
|
|
if 'LAYERFORGE_MAX_FILE_SIZE_MB' in os.environ:
|
|
try:
|
|
self.config['max_file_size_mb'] = int(os.environ['LAYERFORGE_MAX_FILE_SIZE_MB'])
|
|
except ValueError:
|
|
pass
|
|
|
|
if 'LAYERFORGE_BACKUP_COUNT' in os.environ:
|
|
try:
|
|
self.config['backup_count'] = int(os.environ['LAYERFORGE_BACKUP_COUNT'])
|
|
except ValueError:
|
|
pass
|
|
|
|
def configure(self, config):
|
|
"""Konfiguracja loggera"""
|
|
self.config.update(config)
|
|
|
|
# Jeśli włączono logowanie do pliku, upewnij się, że katalog istnieje
|
|
if self.config.get('log_to_file') and self.config.get('log_dir'):
|
|
try:
|
|
os.makedirs(self.config['log_dir'], exist_ok=True)
|
|
except OSError as e:
|
|
# To jest sytuacja krytyczna, więc użyjmy print
|
|
print(f"[CRITICAL] Could not create log directory: {self.config['log_dir']}. Error: {e}")
|
|
traceback.print_exc()
|
|
# Wyłącz logowanie do pliku, aby uniknąć dalszych błędów
|
|
self.config['log_to_file'] = False
|
|
|
|
return self
|
|
|
|
def set_enabled(self, enabled):
|
|
"""Włącz/wyłącz logger globalnie"""
|
|
self.enabled = enabled
|
|
return self
|
|
|
|
def set_global_level(self, level):
|
|
"""Ustaw globalny poziom logowania"""
|
|
self.config['global_level'] = level
|
|
return self
|
|
|
|
def set_module_level(self, module, level):
|
|
"""Ustaw poziom logowania dla konkretnego modułu"""
|
|
self.config['module_settings'][module] = level
|
|
return self
|
|
|
|
def is_level_enabled(self, module, level):
|
|
"""Sprawdź, czy dany poziom logowania jest aktywny dla modułu"""
|
|
if not self.enabled:
|
|
return False
|
|
|
|
# Sprawdź ustawienia modułu, jeśli istnieją
|
|
if module in self.config['module_settings']:
|
|
return level >= self.config['module_settings'][module]
|
|
|
|
# W przeciwnym razie użyj globalnego poziomu
|
|
return level >= self.config['global_level']
|
|
|
|
def _get_logger(self, module):
|
|
"""Pobierz lub utwórz logger dla modułu"""
|
|
if module in self.loggers:
|
|
return self.loggers[module]
|
|
|
|
# Utwórz nowy logger
|
|
logger = logging.getLogger(f"layerforge.{module}")
|
|
logger.setLevel(logging.DEBUG) # Ustaw najniższy poziom, filtrowanie będzie później
|
|
logger.propagate = False
|
|
|
|
# Dodaj handler dla konsoli
|
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
console_formatter = ColoredFormatter(
|
|
fmt='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s',
|
|
datefmt=self.config['timestamp_format'],
|
|
use_colors=self.config['use_colors']
|
|
)
|
|
console_handler.setFormatter(console_formatter)
|
|
logger.addHandler(console_handler)
|
|
|
|
# Dodaj handler dla pliku, jeśli włączono logowanie do pliku
|
|
if self.config['log_to_file']:
|
|
log_file = os.path.join(self.config['log_dir'], f"layerforge_{module}.log")
|
|
file_handler = RotatingFileHandler(
|
|
log_file,
|
|
maxBytes=self.config['max_file_size_mb'] * 1024 * 1024,
|
|
backupCount=self.config['backup_count'],
|
|
encoding='utf-8'
|
|
)
|
|
file_formatter = logging.Formatter(
|
|
fmt='[%(asctime)s] [%(name)s] [%(levelname)s] %(message)s',
|
|
datefmt='%Y-%m-%d %H:%M:%S'
|
|
)
|
|
file_handler.setFormatter(file_formatter)
|
|
logger.addHandler(file_handler)
|
|
|
|
self.loggers[module] = logger
|
|
return logger
|
|
|
|
def log(self, module, level, *args, **kwargs):
|
|
"""Zapisz log"""
|
|
if not self.is_level_enabled(module, level):
|
|
return
|
|
|
|
logger = self._get_logger(module)
|
|
|
|
# Konwertuj argumenty na string
|
|
message = " ".join(str(arg) for arg in args)
|
|
|
|
# Dodaj informacje o wyjątku, jeśli podano
|
|
exc_info = kwargs.get('exc_info', None)
|
|
|
|
# Mapuj poziom LogLevel na poziom logging
|
|
log_level = LEVEL_MAP.get(level, logging.INFO)
|
|
|
|
# Zapisz log
|
|
logger.log(log_level, message, exc_info=exc_info)
|
|
|
|
def debug(self, module, *args, **kwargs):
|
|
"""Log na poziomie DEBUG"""
|
|
self.log(module, LogLevel.DEBUG, *args, **kwargs)
|
|
|
|
def info(self, module, *args, **kwargs):
|
|
"""Log na poziomie INFO"""
|
|
self.log(module, LogLevel.INFO, *args, **kwargs)
|
|
|
|
def warn(self, module, *args, **kwargs):
|
|
"""Log na poziomie WARN"""
|
|
self.log(module, LogLevel.WARN, *args, **kwargs)
|
|
|
|
def error(self, module, *args, **kwargs):
|
|
"""Log na poziomie ERROR"""
|
|
self.log(module, LogLevel.ERROR, *args, **kwargs)
|
|
|
|
def exception(self, module, *args):
|
|
"""Log wyjątku na poziomie ERROR"""
|
|
self.log(module, LogLevel.ERROR, *args, exc_info=True)
|
|
|
|
# Singleton
|
|
logger = LayerForgeLogger()
|
|
|
|
# Funkcje pomocnicze
|
|
def debug(module, *args, **kwargs):
|
|
"""Log na poziomie DEBUG"""
|
|
logger.debug(module, *args, **kwargs)
|
|
|
|
def info(module, *args, **kwargs):
|
|
"""Log na poziomie INFO"""
|
|
logger.info(module, *args, **kwargs)
|
|
|
|
def warn(module, *args, **kwargs):
|
|
"""Log na poziomie WARN"""
|
|
logger.warn(module, *args, **kwargs)
|
|
|
|
def error(module, *args, **kwargs):
|
|
"""Log na poziomie ERROR"""
|
|
logger.error(module, *args, **kwargs)
|
|
|
|
def exception(module, *args):
|
|
"""Log wyjątku na poziomie ERROR"""
|
|
logger.exception(module, *args)
|
|
|
|
# Funkcja do szybkiego włączania/wyłączania debugowania
|
|
def set_debug(enabled=True):
|
|
"""Włącz/wyłącz debugowanie globalnie"""
|
|
if enabled:
|
|
logger.set_global_level(LogLevel.DEBUG)
|
|
else:
|
|
logger.set_global_level(LogLevel.INFO)
|
|
return logger
|
|
|
|
# Funkcja do włączania/wyłączania logowania do pliku
|
|
def set_file_logging(enabled=True, log_dir=None):
|
|
"""Włącz/wyłącz logowanie do pliku"""
|
|
logger.config['log_to_file'] = enabled
|
|
if log_dir:
|
|
logger.config['log_dir'] = log_dir
|
|
os.makedirs(log_dir, exist_ok=True)
|
|
|
|
# Zresetuj loggery, aby zastosować nowe ustawienia
|
|
logger.loggers = {}
|
|
return logger
|