2024-08-30 16:46:37 +00:00
|
|
|
from collections import deque
|
2024-11-09 00:13:34 +00:00
|
|
|
from datetime import datetime
|
|
|
|
import io
|
|
|
|
import logging
|
|
|
|
import sys
|
|
|
|
import threading
|
2024-08-30 16:46:37 +00:00
|
|
|
|
|
|
|
logs = None
|
2024-11-09 00:13:34 +00:00
|
|
|
stdout_interceptor = None
|
|
|
|
stderr_interceptor = None
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
2024-08-30 16:46:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_logs():
|
2024-11-09 00:13:34 +00:00
|
|
|
return logs
|
|
|
|
|
2024-08-30 16:46:37 +00:00
|
|
|
|
2024-11-09 00:13:34 +00:00
|
|
|
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)
|
2024-08-30 16:46:37 +00:00
|
|
|
|
2024-10-04 14:05:34 +00:00
|
|
|
def setup_logger(log_level: str = 'INFO', capacity: int = 300):
|
2024-08-30 16:46:37 +00:00
|
|
|
global logs
|
|
|
|
if logs:
|
|
|
|
return
|
|
|
|
|
2024-11-09 00:13:34 +00:00
|
|
|
# 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)
|
|
|
|
|
2024-08-30 16:46:37 +00:00
|
|
|
# Setup default global logger
|
|
|
|
logger = logging.getLogger()
|
2024-10-04 14:05:34 +00:00
|
|
|
logger.setLevel(log_level)
|
2024-08-30 16:46:37 +00:00
|
|
|
|
|
|
|
stream_handler = logging.StreamHandler()
|
|
|
|
stream_handler.setFormatter(logging.Formatter("%(message)s"))
|
|
|
|
logger.addHandler(stream_handler)
|