MCPcopy Create free account
hub / github.com/SkyworkAI/DeepResearchAgent / Logger

Class Logger

src/logger/log.py:28–242  ·  view source on GitHub ↗

Async logger with synchronous interface. Uses background thread for file I/O to avoid blocking event loop, while maintaining API compatibility.

Source from the content-addressed store, hash-verified

26 DEBUG = logging.DEBUG
27
28class Logger(logging.Logger, metaclass=Singleton):
29 """
30 Async logger with synchronous interface.
31 Uses background thread for file I/O to avoid blocking event loop,
32 while maintaining API compatibility.
33 """
34 def __init__(self, name="logger", level=logging.INFO):
35 # Initialize the parent class
36 super().__init__(name, level)
37
38 # Define a formatter for log messages
39 self.formatter = logging.Formatter(
40 fmt="%(asctime)s - %(name)s:%(levelname)s - %(filename)s:%(lineno)s - %(message)s",
41 datefmt="%Y-%m-%d %H:%M:%S",
42 )
43
44 # Async log writing related
45 self._log_queue: Optional[Queue] = None
46 self._log_thread: Optional[threading.Thread] = None
47 self._stop_event = threading.Event()
48 self._log_path: Optional[str] = None
49 self._initialized = False
50
51 def _log_writer_thread(self, log_path: str):
52 """Background thread: reads logs from queue and writes to file."""
53 with open(log_path, "a", encoding="utf-8") as log_file:
54 while not self._stop_event.is_set():
55 try:
56 # Get log entry from queue (with timeout to avoid blocking forever)
57 log_entry = self._log_queue.get(timeout=0.1)
58 if log_entry is None: # Stop signal
59 break
60
61 # Write to file
62 log_file.write(log_entry)
63 log_file.flush() # Ensure immediate write
64 self._log_queue.task_done()
65
66 except Empty:
67 continue
68 except Exception as e:
69 # If write fails, at least output to stderr
70 import sys
71 print(f"Logger write error: {e}", file=sys.stderr)
72
73 def _enqueue_log(self, level: str, msg: str, *args, **kwargs):
74 """Enqueue log message to background thread (non-blocking)."""
75 if not self._initialized or self._log_queue is None:
76 # Fallback to synchronous logging if not initialized
77 return
78
79 try:
80 # Format log message
81 record = self.makeRecord(
82 self.name,
83 getattr(logging, level.upper()),
84 "", 0, msg, args, None
85 )

Callers 1

log.pyFile · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected