feat(logging): add frontend/backend log controls, unify console logging
- Add frontend_logs_enabled/frontend_log_level and backend_logs_enabled/backend_log_level toggles and selectors in the Options panel (web/resolver/views/options_methods.js) - Sync frontend logging settings from localStorage / server settings into the frontend logger on startup and settings load (download_target_methods.js) - Apply backend logging settings from server settings in the Python backend (__init__.py), loading control state on startup and after saves - Make frontend Logger resilient to stored configs that lack an 'enabled' flag and to legacy/invalid moduleSettings levels (logger.js) - Guard default module logger level initialization against undefined settings (log_funcs.js) - Replace scattered console.log/usages with module-level log.debug calls across resolve_download_methods.js, model_resolver.js, and workflow_state_methods.js - Bump log system package version to 1.5.6 (core + web)
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<img src="https://api.visitorbadge.io/api/combined?path=https%3A%2F%2Fgithub.com%2FAzornes%2FComfyui-Model-Resolver&countColor=%2337d67a&style=for-the-badge&labelStyle=none&labelColor=111827" /></a>
|
||||
<img alt="Python 3.10+" src="https://img.shields.io/badge/Python-3.10+-4B8BBE?labelColor=111827&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMTAiIGhlaWdodD0iMTEwIiB2aWV3Qm94PSIwLjIxIC0wLjA3NyAxMTAgMTEwIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iNjMuODE1OSIgeTE9IjU2LjY4MjkiIHgyPSIxMTguNDkzNCIgeTI9IjEuODIyNSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAtNTMuMjk3NCA2Ni40MzIxKSI%2BPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzg3RUI4Ii8%2BPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzY2OTk0Ii8%2BPC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iOTcuMDQ0NCIgeTE9IjIxLjYzMjEiIHgyPSIxNTUuNjY2NSIgeTI9Ii0zNC41MzA4IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIC01My4yOTc0IDY2LjQzMjEpIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNGRkUwNTIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkMzMzEiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cGF0aCBmaWxsPSJ1cmwoI2EpIiBkPSJNNTUuMDIzLTAuMDc3Yy0yNS45NzEsMC0yNi4yNSwxMC4wODEtMjYuMjUsMTIuMTU2djEyLjU5NGgyNi43NXYzLjc4MUgxOC4xNDhjLTcuOTQ5LDAtMTcuOTM4LDQuODMzLTE3LjkzOCwyNi4yNSwwLDE5LjY3Myw3Ljc5MiwyNy4yODEsMTUuNjU2LDI3LjI4MWg5LjM0NFY2OC44NmMwLTUuNDkxLDIuNzIxLTE1LjY1NiwxNS40MDYtMTUuNjU2aDI2LjUzMWMzLjkwMiwwLDE0LjkwNi0xLjY5NiwxNC45MDYtMTQuNDA2VjE0LjU3OWMuMDAxLTMuMTUzLS41MzgtMTQuNjU2LTI3LjAzLTE0LjY1NnpNNDAuMjczLDguMzkyYzIuNjYyLDAsNC44MTMsMi4xNSw0LjgxMyw0LjgxMywwLDIuNjYxLTIuMTUxLDQuODEzLTQuODEzLDQuODEzcy00LjgxMy0yLjE1MS00LjgxMy00LjgxM2MwLTIuNjYzLDIuMTUxLTQuODEzLDQuODEzLTQuODEzeiIvPjxwYXRoIGZpbGw9InVybCgjYikiIGQ9Ik01NS4zOTcsMTA5LjkyM2MyNS45NTksMCwyNi4yODItMTAuMjcxLDI2LjI4Mi0xMi4xNTZWODUuMTczSDU0Ljg5N3YtMy43ODFoMzcuMzc1YzguMDA5LDAsMTcuOTM4LTQuOTU0LDE3LjkzOC0yNi4yNSwwLTIzLjMyMi0xMC41MzgtMjcuMjgxLTE1LjY1Ni0yNy4yODFIODUuMjF2MTMuMTI1YzAsNS40OTEtMi42MzEsMTUuNjU2LTE1LjQwNiwxNS42NTZINDMuMjcyYy0zLjg5MiwwLTE0LjkwNiwxLjg5Ni0xNC45MDYsMTQuNDA2djI0LjIxOWMwLDUuMjMsMy4xOTYsMTQuNjU2LDI3LjAzMSwxNC42NTZ6TTcwLjE0OCwxMDEuNDU0Yy0yLjY2MiwwLTQuODEzLTIuMTUxLTQuODEzLTQuODEzczIuMTUtNC44MTMsNC44MTMtNC44MTNjMi42NjEsMCw0LjgxMywyLjE1MSw0LjgxMyw0LjgxM3MtMi4xNTIsNC44MTMtNC44MTMsNC44MTN6Ii8%2BPC9zdmc%2B&style=for-the-badge">
|
||||
<img alt="JavaScript" src="https://img.shields.io/badge/-JavaScript-000000?logo=javascript&logoColor=F7DF1E&style=for-the-badge&logoWidth=20">
|
||||
<a href="https://github.com/sponsors/Azornes"><img src="https://img.shields.io/badge/Sponsor-EA4AAA?style=for-the-badge&logo=githubsponsors&logoColor=rede&labelColor=111827" alt="Sponsor"><img src="https://img.shields.io/badge/-%20-181717?style=for-the-badge&logo=github&logoColor=white" alt="GitHub"></a>
|
||||
<a href="https://github.com/sponsors/Azornes"><img src="https://img.shields.io/badge/Sponsor-EA4AAA?style=for-the-badge&logo=githubsponsors&logoColor=magento&labelColor=111827" alt="Sponsor"><img src="https://img.shields.io/badge/-%20-181717?style=for-the-badge&logo=github&logoColor=white" alt="GitHub"></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<strong>🔹 <a href="#%EF%B8%8F-installation">Quick Start</a></strong>
|
||||
|
||||
+34
-2
@@ -15,6 +15,8 @@ from .core.log_system.log_funcs import (
|
||||
log_exception,
|
||||
log_warn,
|
||||
)
|
||||
from .core.log_system.config import LOG_LEVEL as BACKEND_DEFAULT_LOG_LEVEL
|
||||
from .core.log_system.logger import LogLevel, logger as backend_log_controller
|
||||
|
||||
# Web directory for JavaScript interface
|
||||
WEB_DIRECTORY = "./web"
|
||||
@@ -2091,13 +2093,42 @@ class ModelResolverExtension:
|
||||
self.logger.warning(f"Model Resolver: could not read settings file: {exc}")
|
||||
return {}
|
||||
|
||||
def _save_settings(payload: dict) -> None:
|
||||
def _bool_setting(value, default: bool = True) -> bool:
|
||||
if value is None:
|
||||
return default
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if isinstance(value, str):
|
||||
normalized = value.strip().lower()
|
||||
if normalized in {"1", "true", "yes", "on"}:
|
||||
return True
|
||||
if normalized in {"0", "false", "no", "off"}:
|
||||
return False
|
||||
return bool(value)
|
||||
|
||||
def _log_level_setting(value, default: str = BACKEND_DEFAULT_LOG_LEVEL) -> LogLevel:
|
||||
normalized = str(value or default or "INFO").strip().upper()
|
||||
if hasattr(LogLevel, normalized):
|
||||
return getattr(LogLevel, normalized)
|
||||
fallback = str(default or "INFO").strip().upper()
|
||||
return getattr(LogLevel, fallback, LogLevel.INFO)
|
||||
|
||||
def _apply_backend_logging_settings(settings: dict) -> None:
|
||||
enabled = _bool_setting(settings.get("backend_logs_enabled"), True)
|
||||
level = _log_level_setting(settings.get("backend_log_level"))
|
||||
backend_log_controller.set_enabled(enabled)
|
||||
backend_log_controller.set_global_level(level)
|
||||
|
||||
def _save_settings(payload: dict) -> dict:
|
||||
# Merge with existing settings so we never lose keys not sent
|
||||
# by the current request.
|
||||
current = _load_settings()
|
||||
current.update({k: v for k, v in payload.items() if k})
|
||||
with open(_SETTINGS_FILE, "w", encoding="utf-8") as f:
|
||||
_json.dump(current, f, indent=2, ensure_ascii=False)
|
||||
return current
|
||||
|
||||
_apply_backend_logging_settings(_load_settings())
|
||||
|
||||
@routes.get("/model_resolver/settings")
|
||||
async def get_settings_route(request):
|
||||
@@ -2116,7 +2147,8 @@ class ModelResolverExtension:
|
||||
payload = await request.json()
|
||||
if not isinstance(payload, dict):
|
||||
return web.json_response({"error": "Expected JSON object"}, status=400)
|
||||
await asyncio.to_thread(_save_settings, payload)
|
||||
settings = await asyncio.to_thread(_save_settings, payload)
|
||||
_apply_backend_logging_settings(settings)
|
||||
return web.json_response({"success": True})
|
||||
except Exception as e:
|
||||
self.logger.error(f"Model Resolver settings POST error: {e}", exc_info=True)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
@author: Azornes
|
||||
@title: AzLogs
|
||||
@version: 1.5.2
|
||||
@version: 1.5.6
|
||||
@description: Logging Configuration
|
||||
"""
|
||||
# Log level for development.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
@author: Azornes
|
||||
@title: AzLogs
|
||||
@version: 1.5.2
|
||||
@version: 1.5.6
|
||||
@description: Logging Initializator
|
||||
"""
|
||||
# ruff: noqa: T201
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
@author: Azornes
|
||||
@title: AzLogs
|
||||
@version: 1.5.2
|
||||
@version: 1.5.6
|
||||
@description: Logging Setup - Central logging system
|
||||
|
||||
Features:
|
||||
|
||||
@@ -4670,7 +4670,8 @@
|
||||
min-width: 0;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.mr-options-input {
|
||||
.mr-options-input,
|
||||
#model-resolver-modal select.mr-options-input {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
min-height: 42px;
|
||||
@@ -4683,7 +4684,8 @@
|
||||
outline: none;
|
||||
transition: border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
|
||||
}
|
||||
.mr-options-input:focus {
|
||||
.mr-options-input:focus,
|
||||
#model-resolver-modal select.mr-options-input:focus {
|
||||
background: #0f1013;
|
||||
border-color: rgba(78,161,255,0.45);
|
||||
box-shadow: none;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
author: Azornes
|
||||
title: AzLogs
|
||||
version: 1.5.5
|
||||
version: 1.5.6
|
||||
description: Logging Configuration
|
||||
*/
|
||||
// Log level for development.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
author: Azornes
|
||||
title: AzLogs
|
||||
version: 1.5.5
|
||||
version: 1.5.6
|
||||
description: Logging Initializator
|
||||
|
||||
Features:
|
||||
@@ -17,7 +17,10 @@ import { LOG_LEVEL } from './config.js';
|
||||
* @returns {Logger} Object with logging methods
|
||||
*/
|
||||
export function createModuleLogger(moduleName) {
|
||||
logger.setModuleLevel(moduleName, LogLevel[LOG_LEVEL]);
|
||||
logger.config.moduleSettings = logger.config.moduleSettings || {};
|
||||
if (logger.config.moduleSettings[moduleName] === undefined) {
|
||||
logger.setModuleLevel(moduleName, logger.normalizeLevel(logger.config.globalLevel, LogLevel[LOG_LEVEL]));
|
||||
}
|
||||
return {
|
||||
debug: (...args) => logger.debug(moduleName, ...args),
|
||||
info: (...args) => logger.info(moduleName, ...args),
|
||||
|
||||
@@ -13,7 +13,7 @@ logger - Central logging system
|
||||
- Ability to export logs
|
||||
*/
|
||||
|
||||
import { DEFAULT_LOGGER_NAME, LOG_MODULE_NAME, USE_COLORS } from './config.js';
|
||||
import { DEFAULT_LOGGER_NAME, LOG_LEVEL, LOG_MODULE_NAME, USE_COLORS } from './config.js';
|
||||
|
||||
function padStart(str, targetLength, padString) {
|
||||
targetLength = targetLength >> 0;
|
||||
@@ -53,7 +53,8 @@ export const LogLevel = {
|
||||
NONE: 4
|
||||
};
|
||||
const DEFAULT_CONFIG = {
|
||||
globalLevel: LogLevel.INFO,
|
||||
enabled: true,
|
||||
globalLevel: LogLevel[LOG_LEVEL] ?? LogLevel.INFO,
|
||||
moduleSettings: {},
|
||||
useColors: USE_COLORS,
|
||||
saveToStorage: false,
|
||||
@@ -90,6 +91,7 @@ class Logger {
|
||||
this.logs = [];
|
||||
this.enabled = true;
|
||||
this.loadConfig();
|
||||
this.enabled = this.config.enabled !== false;
|
||||
this.loadLogs();
|
||||
}
|
||||
/**
|
||||
@@ -98,6 +100,17 @@ class Logger {
|
||||
*/
|
||||
configure(config) {
|
||||
this.config = { ...this.config, ...config };
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'enabled')) {
|
||||
this.enabled = config.enabled !== false;
|
||||
}
|
||||
this.config.globalLevel = this.normalizeLevel(this.config.globalLevel, DEFAULT_CONFIG.globalLevel);
|
||||
if (!this.config.moduleSettings || typeof this.config.moduleSettings !== 'object') {
|
||||
this.config.moduleSettings = {};
|
||||
} else {
|
||||
Object.entries(this.config.moduleSettings).forEach(([module, level]) => {
|
||||
this.config.moduleSettings[module] = this.normalizeLevel(level, this.config.globalLevel);
|
||||
});
|
||||
}
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
@@ -106,7 +119,9 @@ class Logger {
|
||||
* @param {boolean} enabled - Whether the logger should be enabled
|
||||
*/
|
||||
setEnabled(enabled) {
|
||||
this.enabled = enabled;
|
||||
this.enabled = Boolean(enabled);
|
||||
this.config.enabled = this.enabled;
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
@@ -114,7 +129,32 @@ class Logger {
|
||||
* @param {LogLevels} level - Logging level
|
||||
*/
|
||||
setGlobalLevel(level) {
|
||||
this.config.globalLevel = level;
|
||||
this.config.globalLevel = this.normalizeLevel(level, DEFAULT_CONFIG.globalLevel);
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
normalizeLevel(level, fallback = this.config.globalLevel) {
|
||||
if (typeof level === 'number' && Object.values(LogLevel).includes(level)) {
|
||||
return level;
|
||||
}
|
||||
if (typeof level === 'string') {
|
||||
const normalized = level.trim().toUpperCase();
|
||||
if (Object.prototype.hasOwnProperty.call(LogLevel, normalized)) {
|
||||
return LogLevel[normalized];
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
getLevelName(level = this.config.globalLevel) {
|
||||
const normalizedLevel = this.normalizeLevel(level, this.config.globalLevel);
|
||||
return Object.entries(LogLevel).find(([, value]) => value === normalizedLevel)?.[0] || 'INFO';
|
||||
}
|
||||
setGlobalAndModuleLevel(level) {
|
||||
const normalizedLevel = this.normalizeLevel(level, this.config.globalLevel);
|
||||
this.config.globalLevel = normalizedLevel;
|
||||
Object.keys(this.config.moduleSettings || {}).forEach((module) => {
|
||||
this.config.moduleSettings[module] = normalizedLevel;
|
||||
});
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
@@ -124,7 +164,7 @@ class Logger {
|
||||
* @param {LogLevels} level - Logging level
|
||||
*/
|
||||
setModuleLevel(module, level) {
|
||||
this.config.moduleSettings[module] = level;
|
||||
this.config.moduleSettings[module] = this.normalizeLevel(level, this.config.globalLevel);
|
||||
this.saveConfig();
|
||||
return this;
|
||||
}
|
||||
@@ -597,6 +637,7 @@ class Logger {
|
||||
const storedConfig = localStorage.getItem(LOGGER_CONFIG_KEY);
|
||||
if (storedConfig) {
|
||||
this.config = { ...this.config, ...JSON.parse(storedConfig) };
|
||||
this.configure(this.config);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { app } from "../../../../../scripts/app.js";
|
||||
import { api } from "../../../../../scripts/api.js";
|
||||
import { $el } from "../../../../../scripts/ui.js";
|
||||
import { createModuleLogger } from "../../log_system/log_funcs.js";
|
||||
import { getSvgIcon } from "../../utils/icon_utils.js";
|
||||
|
||||
const log = createModuleLogger('resolve_download_methods');
|
||||
|
||||
export const resolveDownloadMethods = {
|
||||
/**
|
||||
* Resolve a model - resolves ALL nodes that reference this model
|
||||
*/
|
||||
async resolveModel(missing, resolvedModel) {
|
||||
console.log('resolveModel called:', missing?.original_path, '->', resolvedModel?.filename);
|
||||
log.debug('resolveModel called:', missing?.original_path, '->', resolvedModel?.filename);
|
||||
|
||||
if (!resolvedModel) {
|
||||
this.showNotification('No resolved model selected', 'error');
|
||||
@@ -23,7 +27,7 @@ export const resolveDownloadMethods = {
|
||||
|
||||
// Resolve ALL nodes that need this model (all_node_refs contains deduplicated refs)
|
||||
const nodeRefs = missing.all_node_refs || [missing];
|
||||
console.log('nodeRefs count:', nodeRefs?.length, 'is_lora_v2:', nodeRefs?.[0]?.is_lora_v2);
|
||||
log.debug('nodeRefs count:', nodeRefs?.length, 'is_lora_v2:', nodeRefs?.[0]?.is_lora_v2);
|
||||
|
||||
const resolutions = nodeRefs.map(ref => ({
|
||||
node_id: ref.node_id,
|
||||
@@ -51,7 +55,7 @@ export const resolveDownloadMethods = {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Resolve response: success=', data.success, ' missing count:', data.workflow?.nodes?.length);
|
||||
log.debug('Resolve response: success=', data.success, ' missing count:', data.workflow?.nodes?.length);
|
||||
|
||||
if (data.success) {
|
||||
// Update workflow in ComfyUI
|
||||
@@ -1366,7 +1370,7 @@ export const resolveDownloadMethods = {
|
||||
};
|
||||
|
||||
try {
|
||||
console.log('Model Resolver: Search request:', JSON.stringify(searchData));
|
||||
log.debug('Model Resolver: Search request:', JSON.stringify(searchData));
|
||||
|
||||
const response = await api.fetchApi('/model_resolver/search', {
|
||||
method: 'POST',
|
||||
@@ -1379,7 +1383,7 @@ export const resolveDownloadMethods = {
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('Model Resolver: Search response:', JSON.stringify(data));
|
||||
log.debug('Model Resolver: Search response:', JSON.stringify(data));
|
||||
|
||||
if (!this.isBackgroundSearchRunActive(workflowKey, missingSearchKey, searchRunId)) {
|
||||
return { source, stale: true };
|
||||
@@ -1511,9 +1515,9 @@ export const resolveDownloadMethods = {
|
||||
* Resolve URN asynchronously - fetch CivitAI info and update UI
|
||||
*/
|
||||
async resolveUrnAsync(modelId, versionId, loadingElementId, modelUrl) {
|
||||
console.log('resolveUrnAsync called:', modelId, versionId);
|
||||
log.debug('resolveUrnAsync called:', modelId, versionId);
|
||||
if (!modelId || !versionId) {
|
||||
console.log('resolveUrnAsync: missing modelId or versionId');
|
||||
log.debug('resolveUrnAsync: missing modelId or versionId');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1537,7 +1541,7 @@ export const resolveDownloadMethods = {
|
||||
version_id: versionId,
|
||||
civitai_candidate_limit: tokens.civitai_candidate_limit
|
||||
};
|
||||
console.log('resolveUrnAsync payload:', JSON.stringify(payload));
|
||||
log.debug('resolveUrnAsync payload:', JSON.stringify(payload));
|
||||
|
||||
const response = await api.fetchApi('/model_resolver/search', {
|
||||
method: 'POST',
|
||||
@@ -1545,7 +1549,7 @@ export const resolveDownloadMethods = {
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
console.log('resolveUrnAsync response status:', response.status);
|
||||
log.debug('resolveUrnAsync response status:', response.status);
|
||||
if (!response.ok) {
|
||||
throw new Error(`URN resolve failed: ${response.status}`);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
import { app } from "../../../../scripts/app.js";
|
||||
import { api } from "../../../../scripts/api.js";
|
||||
import { $el } from "../../../../scripts/ui.js";
|
||||
import { createModuleLogger } from "../log_system/log_funcs.js";
|
||||
import { logger as frontendLogger } from "../log_system/logger.js";
|
||||
import { loadStylesWhenNeeded } from "../utils/css_loader.js";
|
||||
import { ResolverManagerDialog } from "./resolver_dialog.js";
|
||||
|
||||
const log = createModuleLogger('model_resolver');
|
||||
|
||||
function applyStoredFrontendLoggingPreference() {
|
||||
const stored = localStorage.getItem('ModelResolver.frontendLogsEnabled');
|
||||
if (stored !== null) {
|
||||
frontendLogger.setEnabled(stored !== 'false');
|
||||
}
|
||||
const storedLevel = localStorage.getItem('ModelResolver.frontendLogLevel');
|
||||
if (storedLevel) {
|
||||
frontendLogger.setGlobalAndModuleLevel(frontendLogger.normalizeLevel(storedLevel));
|
||||
}
|
||||
}
|
||||
|
||||
// Main extension class
|
||||
export class ModelResolver {
|
||||
constructor() {
|
||||
@@ -19,6 +34,7 @@ export class ModelResolver {
|
||||
}
|
||||
|
||||
setup = async () => {
|
||||
applyStoredFrontendLoggingPreference();
|
||||
loadStylesWhenNeeded();
|
||||
|
||||
// Remove any existing button
|
||||
@@ -176,7 +192,7 @@ export class ModelResolver {
|
||||
app.menu?.settingsGroup.element.before(this.buttonGroup.element);
|
||||
} catch (e) {
|
||||
// Fallback for older ComfyUI versions without the new button system
|
||||
console.log('Model Resolver: New button system not available, using floating button fallback.');
|
||||
log.debug('Model Resolver: New button system not available, using floating button fallback.');
|
||||
this.createFloatingButton();
|
||||
}
|
||||
}
|
||||
@@ -188,7 +204,7 @@ export class ModelResolver {
|
||||
// Watch for ComfyUI's Missing Models popup and inject our button
|
||||
this.setupMissingModelsPopupObserver();
|
||||
|
||||
console.log('Model Resolver: Missing models popup button injection enabled');
|
||||
log.debug('Model Resolver: Missing models popup button injection enabled');
|
||||
}
|
||||
|
||||
setupActiveWorkflowChangeListeners() {
|
||||
@@ -405,7 +421,7 @@ export class ModelResolver {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Model Resolver: Injected buttons into Missing Models popup');
|
||||
log.debug('Model Resolver: Injected buttons into Missing Models popup');
|
||||
};
|
||||
|
||||
// Small delay to ensure popup is fully rendered
|
||||
@@ -450,7 +466,7 @@ export class ModelResolver {
|
||||
* Mark resolved model items in the popup as linked (green) and hide download buttons
|
||||
*/
|
||||
removeResolvedFromPopup(dialog, resolvedFilenames) {
|
||||
console.log('Model Resolver: Looking for resolved filenames:', resolvedFilenames);
|
||||
log.debug('Model Resolver: Looking for resolved filenames:', resolvedFilenames);
|
||||
|
||||
// Strategy: For each filename, find text nodes containing it,
|
||||
// then find the nearest Download button and mark that row
|
||||
@@ -477,7 +493,7 @@ export class ModelResolver {
|
||||
!btn.id?.includes('model-resolver'));
|
||||
|
||||
if (downloadBtn) {
|
||||
console.log('Model Resolver: Found entry for', filename);
|
||||
log.debug('Model Resolver: Found entry for', filename);
|
||||
this.markEntryAsResolved(parent, downloadBtn);
|
||||
break;
|
||||
}
|
||||
@@ -501,7 +517,7 @@ export class ModelResolver {
|
||||
if (container.dataset.resolved === 'true') return;
|
||||
container.dataset.resolved = 'true';
|
||||
|
||||
console.log('Model Resolver: Marking entry as resolved', container);
|
||||
log.debug('Model Resolver: Marking entry as resolved', container);
|
||||
|
||||
// Add green background/styling to the container
|
||||
container.classList.add('mr-resolved-entry');
|
||||
@@ -574,11 +590,11 @@ export class ModelResolver {
|
||||
widget.callback(resolvedPath, app.graph, node, null, null);
|
||||
}
|
||||
|
||||
console.log(`Model Resolver: Updated node ${nodeId} widget ${widgetIndex} to ${resolvedPath}`);
|
||||
log.debug(`Model Resolver: Updated node ${nodeId} widget ${widgetIndex} to ${resolvedPath}`);
|
||||
} else if (node.widgets_values) {
|
||||
// Fallback: update widgets_values array directly
|
||||
node.widgets_values[widgetIndex] = resolvedPath;
|
||||
console.log(`Model Resolver: Updated node ${nodeId} widgets_values[${widgetIndex}] to ${resolvedPath}`);
|
||||
log.debug(`Model Resolver: Updated node ${nodeId} widgets_values[${widgetIndex}] to ${resolvedPath}`);
|
||||
}
|
||||
|
||||
// Mark node as dirty to trigger redraw
|
||||
@@ -658,7 +674,7 @@ export class ModelResolver {
|
||||
|
||||
// Auto-open dialog if there are missing models
|
||||
if (data.total_missing > 0) {
|
||||
console.log(`Model Resolver: Found ${data.total_missing} missing model(s), opening dialog...`);
|
||||
log.debug(`Model Resolver: Found ${data.total_missing} missing model(s), opening dialog...`);
|
||||
this.openResolverManager();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { app } from "../../../../../scripts/app.js";
|
||||
import { api } from "../../../../../scripts/api.js";
|
||||
import { $el } from "../../../../../scripts/ui.js";
|
||||
import { LOG_LEVEL as DEFAULT_FRONTEND_LOG_LEVEL } from "../../log_system/config.js";
|
||||
import { logger as frontendLogger } from "../../log_system/logger.js";
|
||||
import { getSvgIcon } from "../../utils/icon_utils.js";
|
||||
export const downloadTargetMethods = {
|
||||
/**
|
||||
@@ -860,6 +862,10 @@ export const downloadTargetMethods = {
|
||||
? Math.min(20, Math.max(1, civitaiCandidateLimitRaw))
|
||||
: 5;
|
||||
const search_source_enabled = this.getSearchSourceEnabledMap();
|
||||
const storedFrontendLogsEnabled = localStorage.getItem('ModelResolver.frontendLogsEnabled');
|
||||
const storedBackendLogsEnabled = localStorage.getItem('ModelResolver.backendLogsEnabled');
|
||||
const storedFrontendLogLevel = localStorage.getItem('ModelResolver.frontendLogLevel');
|
||||
const storedBackendLogLevel = localStorage.getItem('ModelResolver.backendLogLevel');
|
||||
|
||||
return {
|
||||
civitai_key: localStorage.getItem('ModelResolver.civitaiApiKey') || '',
|
||||
@@ -873,11 +879,24 @@ export const downloadTargetMethods = {
|
||||
hf_use_brave_fallback: localStorage.getItem('ModelResolver.hfUseBraveFallback') !== 'false',
|
||||
auto_fill_base_model: localStorage.getItem('ModelResolver.autoFillBaseModel') !== 'false',
|
||||
auto_fill_subfolder: localStorage.getItem('ModelResolver.autoFillSubfolder') !== 'false',
|
||||
frontend_logs_enabled: storedFrontendLogsEnabled === null
|
||||
? frontendLogger.enabled !== false
|
||||
: storedFrontendLogsEnabled !== 'false',
|
||||
backend_logs_enabled: storedBackendLogsEnabled === null
|
||||
? true
|
||||
: storedBackendLogsEnabled !== 'false',
|
||||
frontend_log_level: storedFrontendLogLevel || DEFAULT_FRONTEND_LOG_LEVEL,
|
||||
backend_log_level: storedBackendLogLevel || 'DEBUG',
|
||||
civitai_candidate_limit,
|
||||
search_source_enabled
|
||||
};
|
||||
},
|
||||
|
||||
applyFrontendLoggingPreference(enabled = true, levelName = 'DEBUG') {
|
||||
frontendLogger.setEnabled(Boolean(enabled));
|
||||
frontendLogger.setGlobalAndModuleLevel(frontendLogger.normalizeLevel(levelName));
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch settings saved on the server and sync them into localStorage.
|
||||
* Call this once when the dialog initialises so every browser gets the
|
||||
@@ -886,9 +905,17 @@ export const downloadTargetMethods = {
|
||||
async loadSettingsFromServer() {
|
||||
try {
|
||||
const resp = await api.fetchApi('/model_resolver/settings');
|
||||
if (!resp.ok) return;
|
||||
if (!resp.ok) {
|
||||
const tokens = this.getStoredTokens();
|
||||
this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level);
|
||||
return;
|
||||
}
|
||||
const data = await resp.json();
|
||||
if (!data || typeof data !== 'object') return;
|
||||
if (!data || typeof data !== 'object') {
|
||||
const tokens = this.getStoredTokens();
|
||||
this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level);
|
||||
return;
|
||||
}
|
||||
|
||||
// Helper: write to localStorage only when the value from server is
|
||||
// non-empty, so we don't overwrite a key the user already has locally.
|
||||
@@ -919,6 +946,14 @@ export const downloadTargetMethods = {
|
||||
localStorage.setItem('ModelResolver.autoFillSubfolder', data.auto_fill_subfolder ? 'true' : 'false');
|
||||
if (data.civitai_candidate_limit !== undefined)
|
||||
localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${data.civitai_candidate_limit}`);
|
||||
if (data.frontend_logs_enabled !== undefined)
|
||||
localStorage.setItem('ModelResolver.frontendLogsEnabled', data.frontend_logs_enabled ? 'true' : 'false');
|
||||
if (data.backend_logs_enabled !== undefined)
|
||||
localStorage.setItem('ModelResolver.backendLogsEnabled', data.backend_logs_enabled ? 'true' : 'false');
|
||||
if (data.frontend_log_level !== undefined)
|
||||
localStorage.setItem('ModelResolver.frontendLogLevel', String(data.frontend_log_level || 'DEBUG').toUpperCase());
|
||||
if (data.backend_log_level !== undefined)
|
||||
localStorage.setItem('ModelResolver.backendLogLevel', String(data.backend_log_level || 'DEBUG').toUpperCase());
|
||||
|
||||
// Source-enabled flags stored as a nested object
|
||||
if (data.search_source_enabled && typeof data.search_source_enabled === 'object') {
|
||||
@@ -926,8 +961,12 @@ export const downloadTargetMethods = {
|
||||
if (key) localStorage.setItem(key, val ? 'true' : 'false');
|
||||
});
|
||||
}
|
||||
const tokens = this.getStoredTokens();
|
||||
this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level);
|
||||
} catch (err) {
|
||||
console.warn('Model Resolver: could not load settings from server, using localStorage only.', err);
|
||||
const tokens = this.getStoredTokens();
|
||||
this.applyFrontendLoggingPreference(tokens.frontend_logs_enabled, tokens.frontend_log_level);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { app } from "../../../../../scripts/app.js";
|
||||
import { api } from "../../../../../scripts/api.js";
|
||||
import { $el } from "../../../../../scripts/ui.js";
|
||||
import { createModuleLogger } from "../../log_system/log_funcs.js";
|
||||
import { getSvgIcon } from "../../utils/icon_utils.js";
|
||||
|
||||
const log = createModuleLogger('workflow_state_methods');
|
||||
|
||||
export const workflowStateMethods = {
|
||||
getWorkflowSignature(workflow) {
|
||||
if (!workflow) return null;
|
||||
@@ -564,7 +568,7 @@ export const workflowStateMethods = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Model Resolver: active workflow changed, refreshing current tab', {
|
||||
log.debug('Model Resolver: active workflow changed, refreshing current tab', {
|
||||
reason,
|
||||
route: currentRoute,
|
||||
tab: this.activeTab
|
||||
|
||||
@@ -31,6 +31,17 @@ export const optionsMethods = {
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
const logLevelValues = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE'];
|
||||
const normalizeLogLevel = (value) => {
|
||||
const normalized = String(value || '').trim().toUpperCase();
|
||||
return logLevelValues.includes(normalized) ? normalized : 'DEBUG';
|
||||
};
|
||||
const renderLogLevelOptions = (selectedValue) => {
|
||||
const selected = normalizeLogLevel(selectedValue);
|
||||
return logLevelValues
|
||||
.map(level => `<option value="${level}" ${level === selected ? 'selected' : ''}>${level}</option>`)
|
||||
.join('');
|
||||
};
|
||||
this.contentElement.innerHTML = `
|
||||
<div class="mr-options-wrap">
|
||||
<div class="mr-options-shell">
|
||||
@@ -374,6 +385,48 @@ export const optionsMethods = {
|
||||
<h4 class="mr-options-section-title">Maintenance</h4>
|
||||
</div>
|
||||
<div class="mr-options-grid">
|
||||
<div class="mr-options-panel">
|
||||
<div class="mr-options-toggle-list">
|
||||
<label class="mr-options-toggle-row">
|
||||
<div class="mr-options-toggle-copy">
|
||||
<span class="mr-options-toggle-title">Frontend logs <span class="mr-tooltip-badge" data-tooltip="When enabled, Model Resolver writes frontend diagnostic logs to the browser console.">?</span></span>
|
||||
</div>
|
||||
<span class="mr-options-toggle-control">
|
||||
<input id="mr-options-frontend-logs-enabled" class="mr-options-switch-input" type="checkbox" ${tokens.frontend_logs_enabled ? 'checked' : ''}>
|
||||
<span class="mr-options-switch"></span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="mr-options-dependent-block">
|
||||
<div class="mr-options-number-row">
|
||||
<div class="mr-options-number-copy">
|
||||
<span class="mr-options-label">Frontend log level <span class="mr-tooltip-badge" data-tooltip="Minimum frontend log level shown in the browser console. NONE hides all frontend logger output.">?</span></span>
|
||||
</div>
|
||||
<select id="mr-options-frontend-log-level" class="mr-options-input">
|
||||
${renderLogLevelOptions(tokens.frontend_log_level)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<label class="mr-options-toggle-row">
|
||||
<div class="mr-options-toggle-copy">
|
||||
<span class="mr-options-toggle-title">Backend logs <span class="mr-tooltip-badge" data-tooltip="When enabled, Model Resolver writes backend logs to the ComfyUI console and plugin log file.">?</span></span>
|
||||
</div>
|
||||
<span class="mr-options-toggle-control">
|
||||
<input id="mr-options-backend-logs-enabled" class="mr-options-switch-input" type="checkbox" ${tokens.backend_logs_enabled ? 'checked' : ''}>
|
||||
<span class="mr-options-switch"></span>
|
||||
</span>
|
||||
</label>
|
||||
<div class="mr-options-dependent-block">
|
||||
<div class="mr-options-number-row">
|
||||
<div class="mr-options-number-copy">
|
||||
<span class="mr-options-label">Backend log level <span class="mr-tooltip-badge" data-tooltip="Minimum backend log level written by Model Resolver. NONE hides all backend logger output.">?</span></span>
|
||||
</div>
|
||||
<select id="mr-options-backend-log-level" class="mr-options-input">
|
||||
${renderLogLevelOptions(tokens.backend_log_level)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mr-options-panel">
|
||||
<div class="mr-options-stack">
|
||||
<div class="mr-options-db-message">
|
||||
@@ -412,6 +465,10 @@ export const optionsMethods = {
|
||||
const hfUseBraveFallbackInput = this.contentElement.querySelector('#mr-options-hf-use-brave-fallback');
|
||||
const autoFillBaseModelInput = this.contentElement.querySelector('#mr-options-auto-fill-base-model');
|
||||
const autoFillSubfolderInput = this.contentElement.querySelector('#mr-options-auto-fill-subfolder');
|
||||
const frontendLogsEnabledInput = this.contentElement.querySelector('#mr-options-frontend-logs-enabled');
|
||||
const backendLogsEnabledInput = this.contentElement.querySelector('#mr-options-backend-logs-enabled');
|
||||
const frontendLogLevelInput = this.contentElement.querySelector('#mr-options-frontend-log-level');
|
||||
const backendLogLevelInput = this.contentElement.querySelector('#mr-options-backend-log-level');
|
||||
const hfTokenCheckBtn = this.contentElement.querySelector('#mr-options-hf-token-check');
|
||||
const hfTokenCheckStatus = this.contentElement.querySelector('#mr-options-hf-token-check-status');
|
||||
const braveKeyCheckBtn = this.contentElement.querySelector('#mr-options-brave-key-check');
|
||||
@@ -453,6 +510,10 @@ export const optionsMethods = {
|
||||
hfUseBraveFallbackInput,
|
||||
autoFillBaseModelInput,
|
||||
autoFillSubfolderInput,
|
||||
frontendLogsEnabledInput,
|
||||
backendLogsEnabledInput,
|
||||
frontendLogLevelInput,
|
||||
backendLogLevelInput,
|
||||
...sourceEnabledInputs,
|
||||
].filter(Boolean);
|
||||
|
||||
@@ -836,7 +897,7 @@ export const optionsMethods = {
|
||||
setVisibleSection('mr-options-section-sources');
|
||||
|
||||
trackedInputs.forEach((input) => {
|
||||
const eventName = input.type === 'checkbox' ? 'change' : 'input';
|
||||
const eventName = input.type === 'checkbox' || input.tagName === 'SELECT' ? 'change' : 'input';
|
||||
input.addEventListener(eventName, () => {
|
||||
setStatus('You have unsaved changes.', 'is-dirty');
|
||||
});
|
||||
@@ -982,6 +1043,10 @@ export const optionsMethods = {
|
||||
hf_use_brave_fallback: Boolean(hfUseBraveFallbackInput?.checked),
|
||||
auto_fill_base_model: Boolean(autoFillBaseModelInput?.checked),
|
||||
auto_fill_subfolder: Boolean(autoFillSubfolderInput?.checked),
|
||||
frontend_logs_enabled: Boolean(frontendLogsEnabledInput?.checked),
|
||||
backend_logs_enabled: Boolean(backendLogsEnabledInput?.checked),
|
||||
frontend_log_level: normalizeLogLevel(frontendLogLevelInput?.value || tokens.frontend_log_level),
|
||||
backend_log_level: normalizeLogLevel(backendLogLevelInput?.value || tokens.backend_log_level),
|
||||
brave_search_api_key: braveInput?.value || '',
|
||||
search_source_enabled: sourceEnabled,
|
||||
};
|
||||
@@ -998,11 +1063,16 @@ export const optionsMethods = {
|
||||
localStorage.setItem('ModelResolver.hfUseBraveFallback', newSettings.hf_use_brave_fallback ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.autoFillBaseModel', newSettings.auto_fill_base_model ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.autoFillSubfolder', newSettings.auto_fill_subfolder ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.frontendLogsEnabled', newSettings.frontend_logs_enabled ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.backendLogsEnabled', newSettings.backend_logs_enabled ? 'true' : 'false');
|
||||
localStorage.setItem('ModelResolver.frontendLogLevel', newSettings.frontend_log_level);
|
||||
localStorage.setItem('ModelResolver.backendLogLevel', newSettings.backend_log_level);
|
||||
localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${civitaiCandidateLimit}`);
|
||||
Object.entries(sourceEnabled).forEach(([key, val]) => {
|
||||
localStorage.setItem(key, val ? 'true' : 'false');
|
||||
});
|
||||
if (civitaiLimitInput) civitaiLimitInput.value = `${civitaiCandidateLimit}`;
|
||||
this.applyFrontendLoggingPreference(newSettings.frontend_logs_enabled, newSettings.frontend_log_level);
|
||||
|
||||
// 2. Persist to server (survives new browsers / profiles)
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user