ComfyUI/app/logger.py
2024-11-19 15:44:51 +08:00

109 lines
3.0 KiB
Python

import io
import sys
import logging
import threading
from collections import deque
from datetime import datetime
from rich import pretty
from rich.theme import Theme
from rich.console import Console
from rich.logging import RichHandler
logs = None
stdout_interceptor = None
stderr_interceptor = None
LOGFMT = "%(asctime)s %(levelname)s [%(filename)s:%(lineno)s] %(message)s"
formatter = logging.Formatter(LOGFMT)
class LogInterceptor(io.TextIOWrapper):
def __init__(self, stream, *args, **kwargs):
buffer = stream.buffer
encoding = stream.encoding
super().__init__(buffer, *args, **kwargs, encoding=encoding, line_buffering=stream.line_buffering)
self._lock = threading.Lock()
self._flush_callbacks = []
self._logs_since_flush = []
def write(self, data):
entry = {"t": datetime.now().isoformat(), "m": data}
with self._lock:
self._logs_since_flush.append(entry)
# Simple handling for cr to overwrite the last output if it isnt a full line
# else logs just get full of progress messages
if isinstance(data, str) and data.startswith("\r") and not logs[-1]["m"].endswith("\n"):
logs.pop()
logs.append(entry)
super().write(data)
def flush(self):
super().flush()
for cb in self._flush_callbacks:
cb(self._logs_since_flush)
self._logs_since_flush = []
def on_flush(self, callback):
self._flush_callbacks.append(callback)
def get_logs():
return logs
def on_flush(callback):
if stdout_interceptor is not None:
stdout_interceptor.on_flush(callback)
if stderr_interceptor is not None:
stderr_interceptor.on_flush(callback)
def setup_logger(log_level: str = 'INFO', capacity: int = 300):
global logs
if logs:
return
# Override output streams and log to buffer
logs = deque(maxlen=capacity)
global stdout_interceptor
global stderr_interceptor
stdout_interceptor = sys.stdout = LogInterceptor(sys.stdout)
stderr_interceptor = sys.stderr = LogInterceptor(sys.stderr)
# Setup default global logger
logger = logging.getLogger()
logger.setLevel(log_level)
stream_handler = get_rich_hander(log_level)
logger.addHandler(stream_handler)
def get_rich_hander(log_level: str = "INFO") -> RichHandler:
console = Console(
log_time=True,
log_time_format="%Y-%m-%d %H:%M:%S",
theme=Theme({
"log.time": "green",
"inspect.value.border": "black",
"traceback.border": "black",
"traceback.border.syntax_error": "black",
}),
)
pretty.install(console=console)
handler = RichHandler(
console=console,
level=log_level,
markup=False,
show_time=True,
show_level=True,
show_path=True,
rich_tracebacks=True,
enable_link_path=False,
omit_repeated_times=False,
log_time_format="%Y-%m-%d %H:%M:%S",
)
return handler