#!/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="{time:HH:mm:ss} | {level: <8} | 线程{thread} | {message}",
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)