171 lines
5.8 KiB
Python
171 lines
5.8 KiB
Python
![]() |
#!/usr/bin/env python3
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
"""
|
|||
|
现代化日志配置系统 - 使用 loguru
|
|||
|
解决多线程、中文编码、重复输出等问题
|
|||
|
"""
|
|||
|
|
|||
|
import os
|
|||
|
import sys
|
|||
|
from loguru import logger
|
|||
|
import threading
|
|||
|
from pathlib import Path
|
|||
|
|
|||
|
class ModernLogger:
|
|||
|
"""现代化日志管理器,基于loguru实现"""
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
self.initialized = False
|
|||
|
self._lock = threading.Lock()
|
|||
|
|
|||
|
def setup_logging(self, log_dir=".", log_level="INFO", enable_console=True, enable_file=True):
|
|||
|
"""
|
|||
|
设置日志系统
|
|||
|
|
|||
|
参数:
|
|||
|
log_dir: 日志目录
|
|||
|
log_level: 日志级别
|
|||
|
enable_console: 是否启用控制台输出
|
|||
|
enable_file: 是否启用文件输出
|
|||
|
"""
|
|||
|
with self._lock:
|
|||
|
if self.initialized:
|
|||
|
return
|
|||
|
|
|||
|
# 清除默认的logger
|
|||
|
logger.remove()
|
|||
|
|
|||
|
# 设置UTF-8编码环境
|
|||
|
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
|||
|
os.environ['PYTHONLEGACYWINDOWSSTDIO'] = '0'
|
|||
|
|
|||
|
# Windows系统额外配置
|
|||
|
if os.name == 'nt':
|
|||
|
try:
|
|||
|
os.system('chcp 65001 >nul 2>&1')
|
|||
|
if hasattr(sys.stdout, 'reconfigure'):
|
|||
|
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|||
|
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|||
|
except Exception:
|
|||
|
pass
|
|||
|
|
|||
|
# 控制台输出配置
|
|||
|
if enable_console:
|
|||
|
logger.add(
|
|||
|
sys.stdout,
|
|||
|
format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>线程{thread}</cyan> | <level>{message}</level>",
|
|||
|
level=log_level,
|
|||
|
colorize=True,
|
|||
|
backtrace=True,
|
|||
|
diagnose=True,
|
|||
|
enqueue=True, # 多线程安全
|
|||
|
catch=True
|
|||
|
)
|
|||
|
|
|||
|
# 文件输出配置
|
|||
|
if enable_file:
|
|||
|
log_file = Path(log_dir) / "api_{time:YYYY-MM-DD}.log"
|
|||
|
logger.add(
|
|||
|
str(log_file),
|
|||
|
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | 线程{thread} | {message}",
|
|||
|
level=log_level,
|
|||
|
rotation="00:00", # 每天轮转
|
|||
|
retention="7 days", # 保留7天
|
|||
|
compression="zip", # 压缩旧日志
|
|||
|
encoding="utf-8",
|
|||
|
enqueue=True, # 多线程安全
|
|||
|
catch=True
|
|||
|
)
|
|||
|
|
|||
|
# 错误日志单独文件
|
|||
|
error_log_file = Path(log_dir) / "api_error_{time:YYYY-MM-DD}.log"
|
|||
|
logger.add(
|
|||
|
str(error_log_file),
|
|||
|
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | 线程{thread} | {file}:{line} | {message}",
|
|||
|
level="ERROR",
|
|||
|
rotation="00:00",
|
|||
|
retention="30 days", # 错误日志保留更久
|
|||
|
compression="zip",
|
|||
|
encoding="utf-8",
|
|||
|
enqueue=True,
|
|||
|
catch=True
|
|||
|
)
|
|||
|
|
|||
|
self.initialized = True
|
|||
|
logger.info("🚀 现代化日志系统初始化完成")
|
|||
|
logger.info(f"📁 日志目录: {log_dir}")
|
|||
|
logger.info(f"📊 日志级别: {log_level}")
|
|||
|
logger.info(f"🖥️ 控制台输出: {'启用' if enable_console else '禁用'}")
|
|||
|
logger.info(f"📄 文件输出: {'启用' if enable_file else '禁用'}")
|
|||
|
|
|||
|
def get_training_logger(self, task_id: str, model_type: str, product_id: str):
|
|||
|
"""
|
|||
|
获取训练专用的logger
|
|||
|
|
|||
|
参数:
|
|||
|
task_id: 训练任务ID
|
|||
|
model_type: 模型类型
|
|||
|
product_id: 产品ID
|
|||
|
|
|||
|
返回:
|
|||
|
配置好的logger实例
|
|||
|
"""
|
|||
|
return logger.bind(
|
|||
|
task_id=task_id[:8],
|
|||
|
model_type=model_type,
|
|||
|
product_id=product_id,
|
|||
|
context="TRAINING"
|
|||
|
)
|
|||
|
|
|||
|
def get_api_logger(self):
|
|||
|
"""获取API专用的logger"""
|
|||
|
return logger.bind(context="API")
|
|||
|
|
|||
|
def log_training_progress(self, task_id: str, message: str, progress: float = None, **kwargs):
|
|||
|
"""
|
|||
|
记录训练进度日志
|
|||
|
|
|||
|
参数:
|
|||
|
task_id: 任务ID
|
|||
|
message: 日志消息
|
|||
|
progress: 进度百分比
|
|||
|
**kwargs: 额外的日志字段
|
|||
|
"""
|
|||
|
extra_info = ""
|
|||
|
if progress is not None:
|
|||
|
extra_info += f" [进度: {progress:.1f}%]"
|
|||
|
|
|||
|
for key, value in kwargs.items():
|
|||
|
extra_info += f" [{key}: {value}]"
|
|||
|
|
|||
|
logger.bind(
|
|||
|
task_id=task_id[:8],
|
|||
|
context="TRAINING_PROGRESS"
|
|||
|
).info(f"🔥 {message}{extra_info}")
|
|||
|
|
|||
|
# 全局logger实例
|
|||
|
modern_logger = ModernLogger()
|
|||
|
|
|||
|
def get_logger():
|
|||
|
"""获取配置好的logger实例"""
|
|||
|
if not modern_logger.initialized:
|
|||
|
modern_logger.setup_logging()
|
|||
|
return logger
|
|||
|
|
|||
|
def setup_api_logging(log_dir=".", log_level="INFO"):
|
|||
|
"""API服务器日志初始化的便捷函数"""
|
|||
|
modern_logger.setup_logging(
|
|||
|
log_dir=log_dir,
|
|||
|
log_level=log_level,
|
|||
|
enable_console=True,
|
|||
|
enable_file=True
|
|||
|
)
|
|||
|
return get_logger()
|
|||
|
|
|||
|
def get_training_logger(task_id: str, model_type: str, product_id: str):
|
|||
|
"""获取训练专用logger的便捷函数"""
|
|||
|
return modern_logger.get_training_logger(task_id, model_type, product_id)
|
|||
|
|
|||
|
def log_training_progress(task_id: str, message: str, **kwargs):
|
|||
|
"""记录训练进度的便捷函数"""
|
|||
|
modern_logger.log_training_progress(task_id, message, **kwargs)
|