""" 日志工具 - 统一格式,带 task_id 作为 trace_id """ import logging import os import sys from datetime import datetime # 日志目录:fam-core/logs/(相对 src 的上一级),失败则退化为仅 stdout _LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'logs') def setup_logger(name='fam-core', level=logging.INFO): """配置并返回 logger(stdout + 文件双写)""" logger = logging.getLogger(name) if logger.handlers: return logger logger.setLevel(level) formatter = logging.Formatter( '%(asctime)s [%(name)s] %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) logger.addHandler(handler) # 文件输出(daemon 模式下 stdout 不可见,文件是唯一可追溯日志) try: os.makedirs(_LOG_DIR, exist_ok=True) file_handler = logging.FileHandler( os.path.join(_LOG_DIR, 'fam-core.log'), encoding='utf-8') file_handler.setFormatter(formatter) logger.addHandler(file_handler) except OSError: pass # 目录不可写时退化为仅 stdout return logger def log_task(logger, task_id, stage, message, level=logging.INFO, duration_ms=None): """带 task_id 的结构化日志""" parts = [f"[task_id={task_id}]", stage] if duration_ms is not None: parts.append(f"done in {duration_ms}ms") parts.append(message) logger.log(level, ' '.join(parts))