Use log_system helpers for module logging

Replace direct uses of the logging module and module-level loggers with the centralized log_system.log_funcs (log_debug/log_info/log_warn/log_error/log_exception) across multiple modules. Updated imports and removed unused logging.getLogger() instances in downloader, scanner, sources (civitai, huggingface, model_list, popular), workflow_analyzer, workflow_updater, and __init__.py. Minor message/text tweaks and import formatting applied; behavior unchanged aside from routing logs through the shared logging helpers.
This commit is contained in:
Dariusz L
2026-04-22 00:08:51 +02:00
parent e8b49dd184
commit 236ca180e9
9 changed files with 128 additions and 109 deletions
+1 -1
View File
@@ -936,6 +936,6 @@ try:
extension = ModelLinkerExtension()
extension.initialize()
except Exception as e:
logging.error(
log_error(
f"ComfyUI Model Linker extension initialization failed: {e}", exc_info=True
)
+9 -6
View File
@@ -5,7 +5,6 @@ Handles downloading models from various sources with progress tracking.
"""
import os
import logging
import threading
import time
import requests
@@ -13,7 +12,13 @@ from typing import Dict, Any, Optional, Callable, List
from pathlib import Path
from collections import deque
from .log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
from .log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
try:
import folder_paths
@@ -30,8 +35,6 @@ SPEED_HISTORY_SIZE = 5 # Number of samples for smoothing
CHUNK_SIZE = 1024 * 1024 # 1MB chunks for faster downloads
CLI_LOG_INTERVAL = 5 # Log progress to CLI every N seconds
logger = logging.getLogger(__name__)
def format_bytes(bytes_value: int) -> str:
"""Format bytes to human readable string (e.g., 1.5 GB)."""
@@ -107,7 +110,7 @@ def get_download_directory(category: str) -> Optional[str]:
if fallback_paths:
return fallback_paths[0]
except Exception as e:
logger.debug(f"Could not get folder path for {folder_key}: {e}")
log_debug(f"Could not get folder path for {folder_key}: {e}")
return None
@@ -373,7 +376,7 @@ def download_file(
# CLI error log
print(f"[Model Linker] ✗ Download failed: {os.path.basename(dest_path)}")
print(f"[Model Linker] Error: {error_msg}")
logger.error(f"Download error: {e}", exc_info=True)
log_error(f"Download error: {e}", exc_info=True)
return result
+16 -17
View File
@@ -5,17 +5,22 @@ Scans configured model directories and finds available model files.
"""
import os
import logging
from typing import List, Dict, Tuple
from .log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
from .log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
# Import folder_paths lazily - it may not be available until ComfyUI is initialized
try:
import folder_paths
except ImportError:
folder_paths = None
logging.warning("Model Linker: folder_paths not available yet - will retry later")
log_warn("Model Linker: folder_paths not available yet - will retry later")
# Model file extensions to look for
# This matches folder_paths.supported_pt_extensions
@@ -51,7 +56,7 @@ def get_model_directories() -> Dict[str, Tuple[List[str], set]]:
folder_paths = fp
except ImportError:
logging.error("Model Linker: folder_paths still not available")
log_error("Model Linker: folder_paths still not available")
return {}
return folder_paths.folder_names_and_paths.copy()
@@ -81,7 +86,7 @@ def scan_directory(
models = []
if not os.path.exists(directory) or not os.path.isdir(directory):
logging.debug(f"Directory does not exist or is not accessible: {directory}")
log_debug(f"Directory does not exist or is not accessible: {directory}")
return models
try:
@@ -132,7 +137,7 @@ def scan_directory(
}
)
except (OSError, PermissionError) as e:
logging.warning(f"Error scanning directory {directory}: {e}")
log_warn(f"Error scanning directory {directory}: {e}")
return models
@@ -161,7 +166,7 @@ def scan_all_directories() -> List[Dict[str, str]]:
paths = value[0] or []
raw_exts = value[1]
else:
# Unexpected format: treat value as paths
# Unexpected format; treat value as paths
paths = list(value)
raw_exts = []
elif isinstance(value, dict):
@@ -169,7 +174,7 @@ def scan_all_directories() -> List[Dict[str, str]]:
raw_exts = value.get("extensions") or []
else:
# Unknown format; skip category
logging.debug(
log_debug(
f"Unexpected folder_paths format for category {category}: {type(value)}"
)
continue
@@ -180,22 +185,16 @@ def scan_all_directories() -> List[Dict[str, str]]:
elif raw_exts:
extensions = {str(raw_exts).lower()}
except Exception as e:
logging.warning(
f"Error interpreting folder_paths entry for {category}: {e}"
)
log_warn(f"Error interpreting folder_paths entry for {category}: {e}")
continue
for directory_path in paths:
try:
models = scan_directory(directory_path, extensions, category)
all_models.extend(models)
logging.debug(
f"Found {len(models)} models in {category}/{directory_path}"
)
log_debug(f"Found {len(models)} models in {category}/{directory_path}")
except Exception as e:
logging.warning(
f"Error scanning {category} directory {directory_path}: {e}"
)
log_warn(f"Error scanning {category} directory {directory_path}: {e}")
return all_models
+35 -34
View File
@@ -7,15 +7,18 @@ Search and download models from CivitAI.
import os
import re
import json
import logging
import hashlib
import requests
from typing import Dict, Any, Optional, List
from urllib.parse import urlparse, parse_qs, quote
from ..log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
logger = logging.getLogger(__name__)
from ..log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
CIVITAI_API_URL = "https://civitai.com/api/v1"
@@ -109,7 +112,7 @@ def search_civitai_for_file(
response = requests.get(search_url, headers=headers, timeout=15)
if response.status_code != 200:
logger.debug(f"CivitAI search returned {response.status_code}")
log_debug(f"CivitAI search returned {response.status_code}")
return None
data = response.json()
@@ -148,7 +151,7 @@ def search_civitai_for_file(
"match_type": "exact",
}
_search_cache[cache_key] = result
logger.info(f"Found {filename} on CivitAI: {model_name}")
log_info(f"Found {filename} on CivitAI: {model_name}")
return result
# Check for partial match (filename_base in file_base or vice versa)
@@ -171,7 +174,7 @@ def search_civitai_for_file(
"match_type": "partial",
}
_search_cache[cache_key] = result
logger.info(
log_info(
f"Found similar file for {filename} on CivitAI: {model_name}"
)
return result
@@ -181,7 +184,7 @@ def search_civitai_for_file(
return None
except Exception as e:
logger.error(f"CivitAI search error: {e}")
log_error(f"CivitAI search error: {e}")
return None
@@ -267,7 +270,7 @@ def search_civitai(
results.append(result)
except Exception as e:
logger.error(f"CivitAI search error: {e}")
log_error(f"CivitAI search error: {e}")
return results
@@ -307,7 +310,7 @@ def search_civitai_by_hash(
}
except Exception as e:
logger.error(f"CivitAI hash lookup error: {e}")
log_error(f"CivitAI hash lookup error: {e}")
return None
@@ -341,7 +344,7 @@ def resolve_urn(
response = requests.get(url, headers=headers, params=params, timeout=15)
if response.status_code != 200:
logger.warning(f"CivitAI URN resolve failed: {response.status_code}")
log_warn(f"CivitAI URN resolve failed: {response.status_code}")
_urn_cache[cache_key] = None
return None
@@ -349,9 +352,7 @@ def resolve_urn(
versions = data.get("modelVersions", [])
if not versions:
logger.warning(
f"No versions found for model {model_id}/version {version_id}"
)
log_warn(f"No versions found for model {model_id}/version {version_id}")
_urn_cache[cache_key] = None
return None
@@ -363,7 +364,7 @@ def resolve_urn(
break
if not target_version:
logger.warning(f"Version {version_id} not found in model {model_id}")
log_warn(f"Version {version_id} not found in model {model_id}")
_urn_cache[cache_key] = None
return None
@@ -380,7 +381,7 @@ def resolve_urn(
primary_file = files[0] # Fallback to first
if not primary_file:
logger.warning(f"No files found for version {version_id}")
log_warn(f"No files found for version {version_id}")
_urn_cache[cache_key] = None
return None
@@ -395,13 +396,13 @@ def resolve_urn(
}
_urn_cache[cache_key] = result
logger.info(
log_info(
f"Resolved URN model {model_id}@{version_id} → {result['expected_filename']}"
)
return result
except Exception as e:
logger.error(f"CivitAI URN resolve error for {model_id}@{version_id}: {e}")
log_error(f"CivitAI URN resolve error for {model_id}@{version_id}: {e}")
_urn_cache[cache_key] = None
return None
@@ -428,7 +429,7 @@ def _get_sha256_hash(file_path: str) -> Optional[str]:
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
except Exception as e:
logger.error(f"Error computing hash for {file_path}: {e}")
log_error(f"Error computing hash for {file_path}: {e}")
return None
@@ -502,21 +503,21 @@ def get_model_info_by_hash(
}
_hash_cache[cache_key] = result
logger.info(f"Found model by hash {file_hash}: {result.get('model_name')}")
log_info(f"Found model by hash {file_hash}: {result.get('model_name')}")
return result
elif response.status_code == 404:
logger.info(f"Model not found on CivitAI for hash {file_hash}")
log_info(f"Model not found on CivitAI for hash {file_hash}")
_hash_cache[cache_key] = None
return None
else:
logger.warning(
log_warn(
f"CivitAI hash lookup returned {response.status_code} for {file_hash}"
)
return None
except Exception as e:
logger.error(f"Error looking up model by hash {file_hash}: {e}")
log_error(f"Error looking up model by hash {file_hash}: {e}")
return None
cache_key = f"hash_{file_hash}"
@@ -569,21 +570,21 @@ def get_model_info_by_hash(
}
_hash_cache[cache_key] = result
logger.info(f"Found model by hash {file_hash}: {result.get('model_name')}")
log_info(f"Found model by hash {file_hash}: {result.get('model_name')}")
return result
elif response.status_code == 404:
logger.info(f"Model not found on CivitAI for hash {file_hash}")
log_info(f"Model not found on CivitAI for hash {file_hash}")
_hash_cache[cache_key] = None
return None
else:
logger.warning(
log_warn(
f"CivitAI hash lookup returned {response.status_code} for {file_hash}"
)
return None
except Exception as e:
logger.error(f"Error looking up model by hash {file_hash}: {e}")
log_error(f"Error looking up model by hash {file_hash}: {e}")
return None
@@ -687,7 +688,7 @@ def _get_metadata_file_path(model_path: str) -> str:
if name:
path = os.path.join(directory, name)
if os.path.exists(path):
logger.info(f"Found metadata file: {path}")
log_info(f"Found metadata file: {path}")
return path
return ""
@@ -713,10 +714,10 @@ def _read_model_metadata(metadata_path: str) -> Optional[Dict[str, Any]]:
if not data.get("sha256") and not data.get("civitai"):
return None
logger.info(f"Successfully read metadata from: {metadata_path}")
log_info(f"Successfully read metadata from: {metadata_path}")
return data
except Exception as e:
logger.debug(f"Error reading metadata file {metadata_path}: {e}")
log_debug(f"Error reading metadata file {metadata_path}: {e}")
return None
@@ -797,16 +798,16 @@ def get_model_info_for_file(
Returns:
Dict with model info from CivitAI, or None if not found
"""
logger.info(f"get_model_info_for_file called with: {file_path}")
log_info(f"get_model_info_for_file called with: {file_path}")
# First check for metadata file
metadata_path = _get_metadata_file_path(file_path)
logger.info(f"Looking for metadata file, checked: {metadata_path}")
log_info(f"Looking for metadata file, checked: {metadata_path}")
if metadata_path:
metadata = _read_model_metadata(metadata_path)
if metadata:
logger.info(f"Using metadata file for {file_path}")
log_info(f"Using metadata file for {file_path}")
return _metadata_to_model_info(metadata)
else:
# Debug: list all files in the same directory
@@ -816,7 +817,7 @@ def get_model_info_for_file(
metadata_files = [
f for f in files if "metadata" in f.lower() or f.endswith(".json")
]
logger.info(f"Files in {directory}: {metadata_files}")
log_info(f"Files in {directory}: {metadata_files}")
# If no metadata file, compute hash and look up on CivitAI
file_hash = _get_sha256_hash(file_path)
+14 -11
View File
@@ -6,14 +6,17 @@ Search and download models from HuggingFace Hub.
import os
import re
import logging
import requests
from typing import Dict, Any, Optional, List
from urllib.parse import urlparse, quote
from ..log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
logger = logging.getLogger(__name__)
from ..log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
HF_API_URL = "https://huggingface.co/api"
@@ -110,7 +113,7 @@ def search_huggingface_for_file(
response = requests.get(search_url, headers=headers, timeout=10)
if response.status_code != 200:
logger.debug(f"HuggingFace search returned {response.status_code}")
log_debug(f"HuggingFace search returned {response.status_code}")
return None
repos = response.json()
@@ -146,7 +149,7 @@ def search_huggingface_for_file(
"match_type": "exact",
}
_search_cache[cache_key] = result
logger.info(f"Found {filename} on HuggingFace: {repo_id}")
log_info(f"Found {filename} on HuggingFace: {repo_id}")
return result
# Check for partial match (filename_base in file_base or vice versa)
@@ -169,13 +172,13 @@ def search_huggingface_for_file(
"match_type": "partial",
}
_search_cache[cache_key] = result
logger.info(
log_info(
f"Found similar file for {filename} on HuggingFace: {repo_id}/{file_path}"
)
return result
except Exception as e:
logger.debug(f"Error checking repo {repo_id}: {e}")
log_debug(f"Error checking repo {repo_id}: {e}")
continue
# Not found
@@ -183,7 +186,7 @@ def search_huggingface_for_file(
return None
except Exception as e:
logger.error(f"HuggingFace search error: {e}")
log_error(f"HuggingFace search error: {e}")
return None
@@ -228,7 +231,7 @@ def search_huggingface(
)
except Exception as e:
logger.error(f"HuggingFace search error: {e}")
log_error(f"HuggingFace search error: {e}")
return results
@@ -264,6 +267,6 @@ def get_repo_files(repo: str, token: Optional[str] = None) -> List[Dict[str, Any
)
except Exception as e:
logger.error(f"Error getting repo files: {e}")
log_error(f"Error getting repo files: {e}")
return files
+9 -6
View File
@@ -6,13 +6,16 @@ Search the ComfyUI Manager model-list.json database with fuzzy matching.
import os
import json
import logging
from typing import Dict, Any, Optional, List
from difflib import SequenceMatcher
from ..log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
logger = logging.getLogger(__name__)
from ..log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
# Path to metadata directory
METADATA_DIR = os.path.join(
@@ -36,12 +39,12 @@ def _load_model_list() -> List[Dict]:
with open(MODEL_LIST_FILE, "r", encoding="utf-8") as f:
data = json.load(f)
_model_list_cache = data.get("models", [])
logger.info(
log_info(
f"Loaded {len(_model_list_cache)} models from model-list.json"
)
return _model_list_cache
except Exception as e:
logger.error(f"Error loading model list: {e}")
log_error(f"Error loading model list: {e}")
_model_list_cache = []
return _model_list_cache
+9 -6
View File
@@ -6,12 +6,15 @@ Curated list of common models with known download URLs.
import os
import json
import logging
from typing import Dict, Any, Optional, List
from ..log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
logger = logging.getLogger(__name__)
from ..log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
# Path to metadata directory
METADATA_DIR = os.path.join(
@@ -39,7 +42,7 @@ def _load_popular_models() -> Dict[str, Any]:
_popular_models_cache = data.get("models", {})
return _popular_models_cache
except Exception as e:
logger.error(f"Error loading popular models: {e}")
log_error(f"Error loading popular models: {e}")
_popular_models_cache = {}
return _popular_models_cache
@@ -59,7 +62,7 @@ def _load_model_aliases() -> Dict[str, List[str]]:
_model_aliases_cache = data.get("aliases", {})
return _model_aliases_cache
except Exception as e:
logger.error(f"Error loading model aliases: {e}")
log_error(f"Error loading model aliases: {e}")
_model_aliases_cache = {}
return _model_aliases_cache
+14 -10
View File
@@ -5,17 +5,23 @@ Extracts model references from workflow JSON and identifies missing models.
"""
import os
import logging
import re
from typing import List, Dict, Any, Optional
from .log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
from .log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
# Import folder_paths lazily - it may not be available until ComfyUI is initialized
try:
import folder_paths
except ImportError:
folder_paths = None
logging.warning("Model Linker: folder_paths not available yet - will retry later")
log_warn("Model Linker: folder_paths not available yet - will retry later")
# Common model file extensions
@@ -32,8 +38,6 @@ MODEL_EXTENSIONS = {
".gguf",
}
import re
# URN Type to ComfyUI category mapping
URN_TYPE_MAP = {
"checkpoint": "checkpoints",
@@ -140,7 +144,7 @@ def try_resolve_model_path(
folder_paths = fp
except ImportError:
logging.error("Model Linker: folder_paths not available")
log_error("Model Linker: folder_paths not available")
return None
# If categories not provided, try all categories
@@ -272,7 +276,7 @@ def get_node_model_info(node: Dict[str, Any]) -> List[Dict[str, Any]]:
if lora_exists:
break
logging.debug(
log_debug(
f"Lora {name}: exists={lora_exists}, path={lora_full_path}"
)
@@ -445,7 +449,7 @@ def analyze_workflow_models(workflow_json: Dict[str, Any]) -> List[Dict[str, Any
ref["is_top_level"] = True # Flag to indicate this is a top-level node
all_model_refs.extend(model_refs)
except Exception as e:
logging.warning(f"Error analyzing node {node.get('id', 'unknown')}: {e}")
log_warn(f"Error analyzing node {node.get('id', 'unknown')}: {e}")
continue
# Recursively analyze subgraphs (definitions already loaded above)
@@ -457,7 +461,7 @@ def analyze_workflow_models(workflow_json: Dict[str, Any]) -> List[Dict[str, Any
subgraph_name = subgraph.get("name", subgraph_id)
subgraph_nodes = subgraph.get("nodes", [])
logging.debug(
log_debug(
f"Analyzing subgraph: {subgraph_name} (ID: {subgraph_id}) with {len(subgraph_nodes)} nodes"
)
@@ -477,7 +481,7 @@ def analyze_workflow_models(workflow_json: Dict[str, Any]) -> List[Dict[str, Any
ref["is_top_level"] = False # This is inside a subgraph definition
all_model_refs.extend(model_refs)
except Exception as e:
logging.warning(
log_warn(
f"Error analyzing subgraph node {node.get('id', 'unknown')}: {e}"
)
continue
+21 -18
View File
@@ -5,10 +5,15 @@ Updates workflow JSON by replacing model paths in nodes.
"""
import os
import logging
from typing import Dict, Any, List, Optional
from .log_system.log_funcs import log_debug, log_info, log_warn, log_error, log_exception
from .log_system.log_funcs import (
log_debug,
log_info,
log_warn,
log_error,
log_exception,
)
def convert_to_relative_path(
@@ -200,13 +205,13 @@ def update_model_path(
if not node:
location = f"subgraph {subgraph_id}" if subgraph_id else "top-level"
logging.warning(f"Node {node_id} not found in {location}")
log_warn(f"Node {node_id} not found in {location}")
return False
widgets_values = node.get("widgets_values", [])
if widget_index >= len(widgets_values):
logging.warning(f"Widget index {widget_index} out of range for node {node_id}")
log_warn(f"Widget index {widget_index} out of range for node {node_id}")
return False
# Get category from resolved_model if not provided
@@ -216,7 +221,7 @@ def update_model_path(
# Check if this is a LoraManager (LoraLoaderV2) node with is_lora_v2 flag
is_lora_v2 = mapping.get("is_lora_v2") if mapping else False
original_lora_name = mapping.get("original_lora_name") if mapping else None
logging.info(
log_info(
f"@@ update_model_path: node={node_id}, idx={widget_index}, is_lora_v2={is_lora_v2}, original={original_lora_name}"
)
@@ -224,7 +229,7 @@ def update_model_path(
if is_lora_v2 and original_lora_name and widget_index == 2:
# For LoraManager, we need to update the name in the lora list (widgets_values[2])
lora_list = widgets_values[2]
logging.info(
log_info(
f">>> LoraManager: original_lora={original_lora_name}, widget_index={widget_index}, list_type={type(lora_list)}"
)
@@ -241,7 +246,7 @@ def update_model_path(
if "." in new_lora_name:
new_lora_name = new_lora_name.rsplit(".", 1)[0]
logging.debug(
log_debug(
f"LoraManager update: original={original_lora_name}, new={new_lora_name}"
)
@@ -256,14 +261,14 @@ def update_model_path(
if lora_name_in_list == original_stripped:
lora_item["name"] = new_lora_name
updated = True
logging.info(
log_info(
f"Updated LoraManager lora (exact): {original_lora_name} -> {new_lora_name}"
)
break
elif lora_name_in_list.lower() == original_stripped.lower():
lora_item["name"] = new_lora_name
updated = True
logging.info(
log_info(
f"Updated LoraManager lora (case-insensitive): {original_lora_name} -> {new_lora_name}"
)
break
@@ -282,18 +287,18 @@ def update_model_path(
)
widgets_values[1] = new_text
logging.info(
log_info(
f"Updated node {node_id} LoraManager lora list - new text: {widgets_values[1]}"
)
return True
else:
# Log the actual lora list for debugging
logging.warning(
log_warn(
f"Lora '{original_lora_name}' not found in lora list. Available: {[li.get('name') for li in lora_list if isinstance(li, dict)]}"
)
return False
else:
logging.warning(
log_warn(
f"LoraManager widget_index == 2 but lora_list is not a list: {type(lora_list)}"
)
return False
@@ -318,14 +323,12 @@ def update_model_path(
nested_key = mapping.get("nested_key") if mapping else None
if nested_key and isinstance(widgets_values[widget_index], dict):
widgets_values[widget_index][nested_key] = relative_path
logging.debug(
log_debug(
f"Updated node {node_id}, widget {widget_index}[{nested_key}] to: {relative_path}"
)
else:
widgets_values[widget_index] = relative_path
logging.debug(
f"Updated node {node_id}, widget {widget_index} to: {relative_path}"
)
log_debug(f"Updated node {node_id}, widget {widget_index} to: {relative_path}")
return True
@@ -358,7 +361,7 @@ def update_workflow_nodes(
resolved_path = mapping.get("resolved_path")
if not all([node_id is not None, widget_index is not None, resolved_path]):
logging.warning(f"Invalid mapping: {mapping}")
log_warn(f"Invalid mapping: {mapping}")
continue
# Try to get base_directory from resolved_model if provided
@@ -395,5 +398,5 @@ def update_workflow_nodes(
if success:
updated_count += 1
logging.info(f"Updated {updated_count} model paths in workflow")
log_info(f"Updated {updated_count} model paths in workflow")
return workflow