Skip to content

Commit f6e3e9a

Browse files
authored
fix(api-nodes): made logging path to be smaller (#10156)
1 parent 8f4ee99 commit f6e3e9a

File tree

2 files changed

+59
-18
lines changed

2 files changed

+59
-18
lines changed

comfy_api_nodes/apis/client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import asyncio
9696
import logging
9797
import io
98+
import os
9899
import socket
99100
from aiohttp.client_exceptions import ClientError, ClientResponseError
100101
from typing import Dict, Type, Optional, Any, TypeVar, Generic, Callable, Tuple
@@ -499,7 +500,9 @@ async def upload_file(
499500
else:
500501
raise ValueError("File must be BytesIO or str path")
501502

502-
operation_id = f"upload_{upload_url.split('/')[-1]}_{uuid.uuid4().hex[:8]}"
503+
parsed = urlparse(upload_url)
504+
basename = os.path.basename(parsed.path) or parsed.netloc or "upload"
505+
operation_id = f"upload_{basename}_{uuid.uuid4().hex[:8]}"
503506
request_logger.log_request_response(
504507
operation_id=operation_id,
505508
request_method="PUT",

comfy_api_nodes/apis/request_logger.py

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,18 @@
44
import datetime
55
import json
66
import logging
7+
import re
8+
import hashlib
9+
from typing import Any
10+
711
import folder_paths
812

913
# Get the logger instance
1014
logger = logging.getLogger(__name__)
1115

16+
1217
def get_log_directory():
13-
"""
14-
Ensures the API log directory exists within ComfyUI's temp directory
15-
and returns its path.
16-
"""
18+
"""Ensures the API log directory exists within ComfyUI's temp directory and returns its path."""
1719
base_temp_dir = folder_paths.get_temp_directory()
1820
log_dir = os.path.join(base_temp_dir, "api_logs")
1921
try:
@@ -24,42 +26,77 @@ def get_log_directory():
2426
return base_temp_dir
2527
return log_dir
2628

27-
def _format_data_for_logging(data):
29+
30+
def _sanitize_filename_component(name: str) -> str:
31+
if not name:
32+
return "log"
33+
sanitized = re.sub(r"[^A-Za-z0-9._-]+", "_", name) # Replace disallowed characters with underscore
34+
sanitized = sanitized.strip(" ._") # Windows: trailing dots or spaces are not allowed
35+
if not sanitized:
36+
sanitized = "log"
37+
return sanitized
38+
39+
40+
def _short_hash(*parts: str, length: int = 10) -> str:
41+
return hashlib.sha1(("|".join(parts)).encode("utf-8")).hexdigest()[:length]
42+
43+
44+
def _build_log_filepath(log_dir: str, operation_id: str, request_url: str) -> str:
45+
"""Build log filepath. We keep it well under common path length limits aiming for <= 240 characters total."""
46+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")
47+
slug = _sanitize_filename_component(operation_id) # Best-effort human-readable slug from operation_id
48+
h = _short_hash(operation_id or "", request_url or "") # Short hash ties log to the full operation and URL
49+
50+
# Compute how much room we have for the slug given the directory length
51+
# Keep total path length reasonably below ~260 on Windows.
52+
max_total_path = 240
53+
prefix = f"{timestamp}_"
54+
suffix = f"_{h}.log"
55+
if not slug:
56+
slug = "op"
57+
max_filename_len = max(60, max_total_path - len(log_dir) - 1)
58+
max_slug_len = max(8, max_filename_len - len(prefix) - len(suffix))
59+
if len(slug) > max_slug_len:
60+
slug = slug[:max_slug_len].rstrip(" ._-")
61+
return os.path.join(log_dir, f"{prefix}{slug}{suffix}")
62+
63+
64+
def _format_data_for_logging(data: Any) -> str:
2865
"""Helper to format data (dict, str, bytes) for logging."""
2966
if isinstance(data, bytes):
3067
try:
31-
return data.decode('utf-8') # Try to decode as text
68+
return data.decode("utf-8") # Try to decode as text
3269
except UnicodeDecodeError:
3370
return f"[Binary data of length {len(data)} bytes]"
3471
elif isinstance(data, (dict, list)):
3572
try:
3673
return json.dumps(data, indent=2, ensure_ascii=False)
3774
except TypeError:
38-
return str(data) # Fallback for non-serializable objects
75+
return str(data) # Fallback for non-serializable objects
3976
return str(data)
4077

78+
4179
def log_request_response(
4280
operation_id: str,
4381
request_method: str,
4482
request_url: str,
4583
request_headers: dict | None = None,
4684
request_params: dict | None = None,
47-
request_data: any = None,
85+
request_data: Any = None,
4886
response_status_code: int | None = None,
4987
response_headers: dict | None = None,
50-
response_content: any = None,
51-
error_message: str | None = None
88+
response_content: Any = None,
89+
error_message: str | None = None,
5290
):
5391
"""
5492
Logs API request and response details to a file in the temp/api_logs directory.
93+
Filenames are sanitized and length-limited for cross-platform safety.
94+
If we still fail to write, we fall back to appending into api.log.
5595
"""
5696
log_dir = get_log_directory()
57-
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")
58-
filename = f"{timestamp}_{operation_id.replace('/', '_').replace(':', '_')}.log"
59-
filepath = os.path.join(log_dir, filename)
60-
61-
log_content = []
97+
filepath = _build_log_filepath(log_dir, operation_id, request_url)
6298

99+
log_content: list[str] = []
63100
log_content.append(f"Timestamp: {datetime.datetime.now().isoformat()}")
64101
log_content.append(f"Operation ID: {operation_id}")
65102
log_content.append("-" * 30 + " REQUEST " + "-" * 30)
@@ -69,15 +106,15 @@ def log_request_response(
69106
log_content.append(f"Headers:\n{_format_data_for_logging(request_headers)}")
70107
if request_params:
71108
log_content.append(f"Params:\n{_format_data_for_logging(request_params)}")
72-
if request_data:
109+
if request_data is not None:
73110
log_content.append(f"Data/Body:\n{_format_data_for_logging(request_data)}")
74111

75112
log_content.append("\n" + "-" * 30 + " RESPONSE " + "-" * 30)
76113
if response_status_code is not None:
77114
log_content.append(f"Status Code: {response_status_code}")
78115
if response_headers:
79116
log_content.append(f"Headers:\n{_format_data_for_logging(response_headers)}")
80-
if response_content:
117+
if response_content is not None:
81118
log_content.append(f"Content:\n{_format_data_for_logging(response_content)}")
82119
if error_message:
83120
log_content.append(f"Error:\n{error_message}")
@@ -89,6 +126,7 @@ def log_request_response(
89126
except Exception as e:
90127
logger.error(f"Error writing API log to {filepath}: {e}")
91128

129+
92130
if __name__ == '__main__':
93131
# Example usage (for testing the logger directly)
94132
logger.setLevel(logging.DEBUG)

0 commit comments

Comments
 (0)