122 lines
4.6 KiB
Python
122 lines
4.6 KiB
Python
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)) |