import json import logging from colorama import Fore, Style, init from datetime import datetime import numpy as np import sys import inspect import os # 初始化 colorama(自动重置颜色) init(autoreset=True) class CustomLogger: def __init__(self, log_name=None, log_file=None, propagate=False, precise_time=True): """初始化日志记录器 :param log_name: 日志名称标识 :param propagate: 是否传递日志到父记录器 :param precise_time: 是否启用毫秒级时间戳 """ self.logger = logging.getLogger(f"custom_logger_{log_name}") self.logger.setLevel(logging.INFO) self.logger.propagate = propagate self.log_name = log_name self.precise_time = precise_time # 配置日志格式器 self.log_formatter = logging.Formatter( '%(asctime)s - %(log_name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s' ) # 默认添加控制台处理器 if not self.logger.handlers: console_handler = logging.StreamHandler() console_handler.setFormatter(self.log_formatter) self.logger.addHandler(console_handler) # 添加文件处理器 if log_file: # 确保日志目录存在 os.makedirs(os.path.dirname(log_file), exist_ok=True) file_handler = logging.FileHandler(log_file, encoding='utf-8') file_handler.setFormatter(self.log_formatter) self.logger.addHandler(file_handler) def __enter__(self): """上下文管理器入口""" return self def __exit__(self, exc_type, exc_value, traceback): """上下文管理器退出时清理资源""" for handler in self.logger.handlers[:]: self.logger.removeHandler(handler) handler.close() # 彩色日志方法 def log_info(self, message, is_print=True): self._log(message, logging.INFO, Fore.GREEN, is_print) def log_yellow(self, message, is_print=True): self._log(message, logging.INFO, Fore.YELLOW, is_print) def log_blue(self, message, is_print=True): self._log(message, logging.INFO, Fore.CYAN, is_print) def log_warning(self, message, is_print=True): self._log(message, logging.WARNING, Fore.YELLOW, is_print) def log_error(self, message, is_print=True): self._log(message, logging.ERROR, Fore.RED, is_print) def _log(self, message, level, color, is_print=True): """内部日志处理核心方法 :param message: 日志内容(支持字符串/字典/数组等) :param level: 日志级别(如 logging.INFO) :param color: colorama 颜色控制符 :param is_print: 是否打印到控制台 """ # 1. 时间戳格式化 current_time = datetime.now() if self.precise_time: formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S.') + f"{current_time.microsecond // 1000:03d}" else: formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S') # 2. 获取调用者信息(跳过两层栈帧) caller_frame = inspect.stack()[2] filename = os.path.basename(caller_frame.filename) lineno = caller_frame.lineno # 3. 序列化复杂数据类型 if isinstance(message, (dict, list, tuple, np.ndarray)): try: message = json.dumps(message if not isinstance(message, np.ndarray) else message.tolist(), ensure_ascii=False) except (TypeError, ValueError): message = str(message) else: message = str(message) # 4. 控制台彩色输出 if is_print: level_name = logging.getLevelName(level) print(f"{formatted_time} - {self.log_name} - {level_name} - {filename}:{lineno} - {color}{message}{Style.RESET_ALL}") # 5. 写入日志系统 self.logger.log(level, message, extra={'log_name': self.log_name}) if __name__ == "__main__": # 示例用法 with CustomLogger( log_name="测试日志", log_file="logs/MassageRobot_nova5.log", precise_time=True ) as logger: logger.log_info("普通信息(绿色)") logger.log_yellow("黄色提示") logger.log_blue("蓝色消息") logger.log_warning("警告信息(黄色)") logger.log_error("错误信息(红色)") # 测试复杂数据类型 # logger.log_info({"key": "值", "数组": [1, 2, 3]}) # logger.log_info(np.random.rand(3, 3))