Add centralized logging system with CSS loader
- Implement core logger (logger.js) with log levels (DEBUG/INFO/WARN/ERROR) - Add LoggerUtils.js for module-specific logger initialization - Add configuration module (config.js) with DEBUG level default - Add CSS loader for dynamic stylesheet loading - Features: color-coded console output, localStorage persistence, log export, per-module log level control
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* CSS Loader
|
||||
* Loads all CSS files for the resolution master components
|
||||
*/
|
||||
|
||||
import { addStylesheet, getUrl } from "./utils/ResourceManager.js";
|
||||
import { createModuleLogger } from "./utils/LoggerUtils.js";
|
||||
|
||||
const log = createModuleLogger('CSSLoader');
|
||||
|
||||
/**
|
||||
* Loads all CSS files for the application
|
||||
*/
|
||||
export function loadAllStyles() {
|
||||
try {
|
||||
log.info('Loading CSS files...');
|
||||
|
||||
// Load all CSS files using getUrl for proper path resolution
|
||||
const cssFiles = [
|
||||
'./css/css-variables.css', // CSS Custom Properties - MUST BE FIRST!
|
||||
'./css/common-components.css', // Shared component styles using cascade
|
||||
'./css/aspect-ratio-columns.css', // Shared aspect ratio column styles
|
||||
'./css/preset-manager-dialog.css',
|
||||
'./css/searchable-dropdown.css',
|
||||
'./css/rename-dialog.css',
|
||||
'./css/json-editor-dialog.css',
|
||||
'./css/preset-ui-components.css',
|
||||
'./css/preset-list-renderer.css',
|
||||
'./css/preset-add-view.css',
|
||||
'./css/drag-drop.css'
|
||||
];
|
||||
|
||||
cssFiles.forEach(file => {
|
||||
addStylesheet(getUrl(file));
|
||||
log.debug(`Loaded: ${file}`);
|
||||
});
|
||||
|
||||
log.info('All CSS files loaded successfully');
|
||||
} catch (error) {
|
||||
log.error('Error loading CSS files:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function when the ResolutionMaster node is actually created/used
|
||||
* to load styles only when needed, preventing global UI interference
|
||||
*/
|
||||
export function loadStylesWhenNeeded() {
|
||||
loadAllStyles();
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* LoggerUtils - Centralizacja inicjalizacji loggerów
|
||||
* Eliminuje powtarzalny kod inicjalizacji loggera w każdym module
|
||||
*/
|
||||
import { logger, LogLevel } from "./logger.js";
|
||||
import { LOG_LEVEL } from './config.js';
|
||||
/**
|
||||
* Tworzy obiekt loggera dla modułu z predefiniowanymi metodami
|
||||
* @param {string} moduleName - Nazwa modułu
|
||||
* @returns {Logger} Obiekt z metodami logowania
|
||||
*/
|
||||
export function createModuleLogger(moduleName) {
|
||||
logger.setModuleLevel(moduleName, LogLevel[LOG_LEVEL]);
|
||||
return {
|
||||
debug: (...args) => logger.debug(moduleName, ...args),
|
||||
info: (...args) => logger.info(moduleName, ...args),
|
||||
warn: (...args) => logger.warn(moduleName, ...args),
|
||||
error: (...args) => logger.error(moduleName, ...args)
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Tworzy logger z automatycznym wykrywaniem nazwy modułu z URL
|
||||
* @returns {Logger} Obiekt z metodami logowania
|
||||
*/
|
||||
export function createAutoLogger() {
|
||||
const stack = new Error().stack;
|
||||
const match = stack?.match(/\/([^\/]+)\.js/);
|
||||
const moduleName = match ? match[1] : 'Unknown';
|
||||
return createModuleLogger(moduleName);
|
||||
}
|
||||
/**
|
||||
* Wrapper dla operacji z automatycznym logowaniem błędów
|
||||
* @param {Function} operation - Operacja do wykonania
|
||||
* @param {Logger} log - Obiekt loggera
|
||||
* @param {string} operationName - Nazwa operacji (dla logów)
|
||||
* @returns {Function} Opakowana funkcja
|
||||
*/
|
||||
export function withErrorLogging(operation, log, operationName) {
|
||||
return async function (...args) {
|
||||
try {
|
||||
log.debug(`Starting ${operationName}`);
|
||||
const result = await operation.apply(this, args);
|
||||
log.debug(`Completed ${operationName}`);
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`Error in ${operationName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Decorator dla metod klasy z automatycznym logowaniem
|
||||
* @param {Logger} log - Obiekt loggera
|
||||
* @param {string} methodName - Nazwa metody
|
||||
*/
|
||||
export function logMethod(log, methodName) {
|
||||
return function (target, propertyKey, descriptor) {
|
||||
const originalMethod = descriptor.value;
|
||||
descriptor.value = async function (...args) {
|
||||
try {
|
||||
log.debug(`${methodName || propertyKey} started`);
|
||||
const result = await originalMethod.apply(this, args);
|
||||
log.debug(`${methodName || propertyKey} completed`);
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
log.error(`${methodName || propertyKey} failed:`, error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Log level for development.
|
||||
// Possible values: 'DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE'
|
||||
export const LOG_LEVEL = 'DEBUG';
|
||||
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Logger - Central logging system for ComfyUI-ResolutionMaster
|
||||
*
|
||||
* Features:
|
||||
* - Multiple log levels (DEBUG, INFO, WARN, ERROR)
|
||||
* - Ability to enable/disable logging globally or per module
|
||||
* - Colorful logs in the console
|
||||
* - Ability to save logs to localStorage
|
||||
* - Ability to export logs
|
||||
*/
|
||||
function padStart(str, targetLength, padString) {
|
||||
targetLength = targetLength >> 0;
|
||||
padString = String(padString || ' ');
|
||||
if (str.length > targetLength) {
|
||||
return String(str);
|
||||
}
|
||||
else {
|
||||
targetLength = targetLength - str.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length);
|
||||
}
|
||||
return padString.slice(0, targetLength) + String(str);
|
||||
}
|
||||
}
|
||||
export const LogLevel = {
|
||||
DEBUG: 0,
|
||||
INFO: 1,
|
||||
WARN: 2,
|
||||
ERROR: 3,
|
||||
NONE: 4
|
||||
};
|
||||
const DEFAULT_CONFIG = {
|
||||
globalLevel: LogLevel.INFO,
|
||||
moduleSettings: {},
|
||||
useColors: true,
|
||||
saveToStorage: false,
|
||||
maxStoredLogs: 1000,
|
||||
timestampFormat: 'HH:mm:ss',
|
||||
storageKey: 'ResolutionMaster_logs'
|
||||
};
|
||||
const COLORS = {
|
||||
[LogLevel.DEBUG]: '#9e9e9e',
|
||||
[LogLevel.INFO]: '#2196f3',
|
||||
[LogLevel.WARN]: '#ff9800',
|
||||
[LogLevel.ERROR]: '#f44336',
|
||||
};
|
||||
const LEVEL_NAMES = {
|
||||
[LogLevel.DEBUG]: 'DEBUG',
|
||||
[LogLevel.INFO]: 'INFO',
|
||||
[LogLevel.WARN]: 'WARN',
|
||||
[LogLevel.ERROR]: 'ERROR',
|
||||
};
|
||||
class Logger {
|
||||
constructor() {
|
||||
this.config = { ...DEFAULT_CONFIG };
|
||||
this.logs = [];
|
||||
this.enabled = true;
|
||||
this.loadConfig();
|
||||
}
|
||||
/**
|
||||
* Configure the logger
|
||||
* @param {Partial<LoggerConfig>} config - Configuration object
|
||||
*/
|
||||
configure(config) {
|
||||
this.config = { ...this.config, ...config };
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Enable/disable logger globally
|
||||
* @param {boolean} enabled - Whether the logger should be enabled
|
||||
*/
|
||||
setEnabled(enabled) {
|
||||
this.enabled = enabled;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set global logging level
|
||||
* @param {LogLevels} level - Logging level
|
||||
*/
|
||||
setGlobalLevel(level) {
|
||||
this.config.globalLevel = level;
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Set logging level for a specific module
|
||||
* @param {string} module - Module name
|
||||
* @param {LogLevels} level - Logging level
|
||||
*/
|
||||
setModuleLevel(module, level) {
|
||||
this.config.moduleSettings[module] = level;
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Check if a given logging level is active for a module
|
||||
* @param {string} module - Module name
|
||||
* @param {LogLevels} level - Logging level to check
|
||||
* @returns {boolean} - Whether the level is active
|
||||
*/
|
||||
isLevelEnabled(module, level) {
|
||||
if (!this.enabled)
|
||||
return false;
|
||||
if (this.config.moduleSettings[module] !== undefined) {
|
||||
return level >= this.config.moduleSettings[module];
|
||||
}
|
||||
return level >= this.config.globalLevel;
|
||||
}
|
||||
/**
|
||||
* Format timestamp
|
||||
* @returns {string} - Formatted timestamp
|
||||
*/
|
||||
formatTimestamp() {
|
||||
const now = new Date();
|
||||
const format = this.config.timestampFormat;
|
||||
return format
|
||||
.replace('HH', padStart(String(now.getHours()), 2, '0'))
|
||||
.replace('mm', padStart(String(now.getMinutes()), 2, '0'))
|
||||
.replace('ss', padStart(String(now.getSeconds()), 2, '0'))
|
||||
.replace('SSS', padStart(String(now.getMilliseconds()), 3, '0'));
|
||||
}
|
||||
/**
|
||||
* Save log
|
||||
* @param {string} module - Module name
|
||||
* @param {LogLevels} level - Logging level
|
||||
* @param {any[]} args - Arguments to log
|
||||
*/
|
||||
log(module, level, ...args) {
|
||||
if (!this.isLevelEnabled(module, level))
|
||||
return;
|
||||
const timestamp = this.formatTimestamp();
|
||||
const levelName = LEVEL_NAMES[level];
|
||||
const logData = {
|
||||
timestamp,
|
||||
module,
|
||||
level,
|
||||
levelName,
|
||||
args,
|
||||
time: new Date()
|
||||
};
|
||||
if (this.config.saveToStorage) {
|
||||
this.logs.push(logData);
|
||||
if (this.logs.length > this.config.maxStoredLogs) {
|
||||
this.logs.shift();
|
||||
}
|
||||
this.saveLogs();
|
||||
}
|
||||
this.printToConsole(logData);
|
||||
}
|
||||
/**
|
||||
* Display log to console
|
||||
* @param {LogData} logData - Log data
|
||||
*/
|
||||
printToConsole(logData) {
|
||||
const { timestamp, module, level, levelName, args } = logData;
|
||||
const prefix = `[${timestamp}] [${module}] [${levelName}]`;
|
||||
if (this.config.useColors && typeof console.log === 'function') {
|
||||
const color = COLORS[level] || '#000000';
|
||||
console.log(`%c${prefix}`, `color: ${color}; font-weight: bold;`, ...args);
|
||||
return;
|
||||
}
|
||||
console.log(prefix, ...args);
|
||||
}
|
||||
/**
|
||||
* Save logs to localStorage
|
||||
*/
|
||||
saveLogs() {
|
||||
if (typeof localStorage !== 'undefined' && this.config.saveToStorage) {
|
||||
try {
|
||||
const simplifiedLogs = this.logs.map((log) => ({
|
||||
t: log.timestamp,
|
||||
m: log.module,
|
||||
l: log.level,
|
||||
a: log.args.map((arg) => {
|
||||
if (typeof arg === 'object') {
|
||||
try {
|
||||
return JSON.stringify(arg);
|
||||
}
|
||||
catch (e) {
|
||||
return String(arg);
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
})
|
||||
}));
|
||||
localStorage.setItem(this.config.storageKey, JSON.stringify(simplifiedLogs));
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failed to save logs to localStorage:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Load logs from localStorage
|
||||
*/
|
||||
loadLogs() {
|
||||
if (typeof localStorage !== 'undefined' && this.config.saveToStorage) {
|
||||
try {
|
||||
const storedLogs = localStorage.getItem(this.config.storageKey);
|
||||
if (storedLogs) {
|
||||
this.logs = JSON.parse(storedLogs);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failed to load logs from localStorage:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Save configuration to localStorage
|
||||
*/
|
||||
saveConfig() {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
localStorage.setItem('ResolutionMaster_logger_config', JSON.stringify(this.config));
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failed to save logger config to localStorage:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Load configuration from localStorage
|
||||
*/
|
||||
loadConfig() {
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
const storedConfig = localStorage.getItem('ResolutionMaster_logger_config');
|
||||
if (storedConfig) {
|
||||
this.config = { ...this.config, ...JSON.parse(storedConfig) };
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failed to load logger config from localStorage:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Clear all logs
|
||||
*/
|
||||
clearLogs() {
|
||||
this.logs = [];
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.removeItem(this.config.storageKey);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Export logs to file
|
||||
* @param {'json' | 'txt'} format - Export format
|
||||
*/
|
||||
exportLogs(format = 'json') {
|
||||
if (this.logs.length === 0) {
|
||||
console.warn('No logs to export');
|
||||
return;
|
||||
}
|
||||
let content;
|
||||
let mimeType;
|
||||
let extension;
|
||||
if (format === 'json') {
|
||||
content = JSON.stringify(this.logs, null, 2);
|
||||
mimeType = 'application/json';
|
||||
extension = 'json';
|
||||
}
|
||||
else {
|
||||
content = this.logs.map((log) => `[${log.timestamp}] [${log.module}] [${log.levelName}] ${log.args.join(' ')}`).join('\n');
|
||||
mimeType = 'text/plain';
|
||||
extension = 'txt';
|
||||
}
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `ResolutionMaster_logs_${new Date().toISOString().replace(/[:.]/g, '-')}.${extension}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
/**
|
||||
* Log at DEBUG level
|
||||
* @param {string} module - Module name
|
||||
* @param {any[]} args - Arguments to log
|
||||
*/
|
||||
debug(module, ...args) {
|
||||
this.log(module, LogLevel.DEBUG, ...args);
|
||||
}
|
||||
/**
|
||||
* Log at INFO level
|
||||
* @param {string} module - Module name
|
||||
* @param {any[]} args - Arguments to log
|
||||
*/
|
||||
info(module, ...args) {
|
||||
this.log(module, LogLevel.INFO, ...args);
|
||||
}
|
||||
/**
|
||||
* Log at WARN level
|
||||
* @param {string} module - Module name
|
||||
* @param {any[]} args - Arguments to log
|
||||
*/
|
||||
warn(module, ...args) {
|
||||
this.log(module, LogLevel.WARN, ...args);
|
||||
}
|
||||
/**
|
||||
* Log at ERROR level
|
||||
* @param {string} module - Module name
|
||||
* @param {any[]} args - Arguments to log
|
||||
*/
|
||||
error(module, ...args) {
|
||||
this.log(module, LogLevel.ERROR, ...args);
|
||||
}
|
||||
}
|
||||
export const logger = new Logger();
|
||||
export const debug = (module, ...args) => logger.debug(module, ...args);
|
||||
export const info = (module, ...args) => logger.info(module, ...args);
|
||||
export const warn = (module, ...args) => logger.warn(module, ...args);
|
||||
export const error = (module, ...args) => logger.error(module, ...args);
|
||||
if (typeof window !== 'undefined') {
|
||||
window.ResolutionMasterLogger = logger;
|
||||
}
|
||||
export default logger;
|
||||
Reference in New Issue
Block a user