Files
Comfyui-LayerForge/python/logger.py
Dariusz L 0078faf1fd Set default log level to NONE and improve logger checks
Changed the default log level to LogLevel.NONE in both Python and JavaScript logger utilities. Updated LayerForgeLogger to fully disable logging when the effective level is NONE, ensuring no logs are emitted for disabled modules.
2025-06-28 06:11:12 +02:00

324 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
# Ustal efektywny poziom logowania, biorąc pod uwagę ustawienia modułu i globalne
effective_level = self.config['module_settings'].get(module, self.config['global_level'])
# Jeśli efektywny poziom to NONE, logowanie jest całkowicie wyłączone
if effective_level == LogLevel.NONE:
return False
# W przeciwnym razie sprawdź, czy poziom loga jest wystarczająco wysoki
return level >= effective_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