from __future__ import annotations import logging from pathlib import Path from typing import Optional class _NodeLogFilter(logging.Filter): def filter(self, record: logging.LogRecord) -> bool: message = record.getMessage() if "node=" in message: return False if "节点 " in message and "状态" in message: return False return True def init_pipeline_logger( *, log_file_path: Optional[str] = None, level: int = logging.INFO, mirror_console: bool = True, replace_handlers: bool = True, suppress_node_logs: bool = True, ) -> logging.Logger: logger = logging.getLogger("sync_state_machine") logger.setLevel(level) logger.propagate = False if replace_handlers: clear_pipeline_logger_handlers(logger) formatter = logging.Formatter("%(message)s") node_filter = _NodeLogFilter() if suppress_node_logs else None if mirror_console: console_handler = logging.StreamHandler() console_handler.setLevel(level) console_handler.setFormatter(formatter) if node_filter is not None: console_handler.addFilter(node_filter) logger.addHandler(console_handler) if log_file_path: path = Path(log_file_path) path.parent.mkdir(parents=True, exist_ok=True) file_handler = logging.FileHandler(path, mode="w", encoding="utf-8") file_handler.setLevel(level) file_handler.setFormatter(formatter) if node_filter is not None: file_handler.addFilter(node_filter) logger.addHandler(file_handler) return logger def clear_pipeline_logger_handlers(logger: Optional[logging.Logger] = None) -> None: target = logger or logging.getLogger("sync_state_machine") handlers = list(target.handlers) for handler in handlers: target.removeHandler(handler) try: handler.close() except Exception: pass