ShopTRAINING/server/utils/logging_config.py
2025-07-02 11:05:23 +08:00

171 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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)