VWED_server/utils/logger.py
2025-07-14 10:29:37 +08:00

280 lines
8.9 KiB
Python
Raw Permalink 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.

# utils/logger.py
import logging
import os
import traceback
from logging.handlers import RotatingFileHandler
from config.settings import LogConfig
# 延迟导入告警同步服务,避免循环导入
_alert_sync_imported = False
_sync_alert_from_record = None
def _import_alert_sync():
"""延迟导入告警同步服务"""
global _alert_sync_imported, _sync_alert_from_record
if not _alert_sync_imported:
try:
from utils.alert_sync import sync_alert_from_record
_sync_alert_from_record = sync_alert_from_record
_alert_sync_imported = True
except ImportError as e:
print(f"告警同步模块导入失败: {e}")
_alert_sync_imported = True # 避免重复尝试
class LevelBasedFormatter(logging.Formatter):
"""
基于日志级别的格式化器
对不同级别的日志使用不同的格式
"""
def __init__(self):
# 普通日志格式
self.normal_format = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
# 详细日志格式(用于警告和错误)
self.detailed_format = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(funcName)s() - %(message)s"
)
def format(self, record):
# 如果是警告或错误级别,使用详细格式
if record.levelno >= logging.WARNING:
return self.detailed_format.format(record)
else:
return self.normal_format.format(record)
class AlertSyncHandler(logging.Handler):
"""
告警同步处理器
拦截WARNING及以上级别的日志同步到主系统
"""
def __init__(self):
super().__init__()
self.setLevel(logging.WARNING) # 只处理WARNING及以上级别
def emit(self, record):
"""
处理日志记录,同步告警到主系统
Args:
record: 日志记录
"""
try:
# 延迟导入告警同步服务
_import_alert_sync()
# 如果告警同步服务可用,同步告警
if _sync_alert_from_record:
_sync_alert_from_record(record)
except Exception:
# 静默处理错误,避免影响正常日志记录
pass
def setup_logger():
"""设置日志"""
# 获取日志配置
LOG_CONFIG = LogConfig.as_dict()
# 创建日志目录
log_dir = os.path.dirname(LOG_CONFIG["file"])
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# 设置日志级别
log_level = getattr(logging, LOG_CONFIG["level"].upper(), logging.INFO)
# 设置日志格式 - 增加行号、函数名等信息
log_format = logging.Formatter(LOG_CONFIG.get("format", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
# 为警告和错误级别创建特殊格式
detailed_format = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(funcName)s() - %(message)s"
)
# 创建根日志记录器
root_logger = logging.getLogger()
root_logger.setLevel(log_level)
# 清除现有处理器
for handler in root_logger.handlers[:]:
root_logger.removeHandler(handler)
# 添加控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(log_format)
# 为警告及以上级别设置详细格式
console_handler.setLevel(logging.INFO)
root_logger.addHandler(console_handler)
# 添加专门的错误控制台处理器,显示更多详细信息
error_console_handler = logging.StreamHandler()
error_console_handler.setFormatter(detailed_format)
error_console_handler.setLevel(logging.WARNING) # 只处理警告及以上级别
root_logger.addHandler(error_console_handler)
# 添加统一的文件处理器,使用基于级别的格式化器
file_handler = RotatingFileHandler(
LOG_CONFIG["file"],
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=5,
encoding='utf-8'
)
# 使用自定义的格式化器,根据日志级别自动选择格式
file_handler.setFormatter(LevelBasedFormatter())
root_logger.addHandler(file_handler)
# 添加告警同步处理器
alert_sync_handler = AlertSyncHandler()
root_logger.addHandler(alert_sync_handler)
# 设置第三方库的日志级别
logging.getLogger("werkzeug").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
return root_logger
class Logger:
"""
日志记录器类
封装不同级别的日志记录功能
"""
def __init__(self, name):
"""
初始化日志记录器
Args:
name (str): 日志记录器名称,通常为模块名称
"""
# 获取指定名称的日志记录器
self.logger = logging.getLogger(name)
self.name = name
def debug(self, message, *args, **kwargs):
"""
记录调试日志
Args:
message: 日志消息
*args: 位置参数
**kwargs: 关键字参数
"""
self.logger.debug(message, *args, **kwargs)
def info(self, message, *args, **kwargs):
"""
记录一般信息日志
Args:
message: 日志消息
*args: 位置参数
**kwargs: 关键字参数
"""
self.logger.info(message, *args, **kwargs)
def warning(self, message, *args, exc_info=False, stack_info=False, **kwargs):
"""
记录警告日志
Args:
message: 日志消息
*args: 位置参数
exc_info: 是否包含异常信息默认False
stack_info: 是否包含堆栈信息默认False
**kwargs: 关键字参数
"""
# 默认启用堆栈信息以便显示行号
# 如果不想显示整个堆栈跟踪可以保持stack_info为False系统会自动记录行号
self.logger.warning(message, *args, exc_info=exc_info, stack_info=stack_info, **kwargs)
def error(self, message, *args, exc_info=True, stack_info=True, **kwargs):
"""
记录错误日志
Args:
message: 日志消息
*args: 位置参数
exc_info: 是否包含异常信息默认True
stack_info: 是否包含堆栈信息默认True
**kwargs: 关键字参数
"""
# 由于我们已经在日志格式中添加了行号和文件名,这里不需要特殊处理
self.logger.error(message, *args, exc_info=exc_info, stack_info=stack_info, **kwargs)
def critical(self, message, *args, exc_info=True, stack_info=True, **kwargs):
"""
记录严重错误日志
Args:
message: 日志消息
*args: 位置参数
exc_info: 是否包含异常信息默认True
stack_info: 是否包含堆栈信息默认True
**kwargs: 关键字参数
"""
self.logger.critical(message, *args, exc_info=exc_info, stack_info=stack_info, **kwargs)
def exception(self, message, *args, **kwargs):
"""
记录异常日志,自动包含异常堆栈信息
Args:
message: 日志消息
*args: 位置参数
**kwargs: 关键字参数
"""
self.logger.exception(message, *args, **kwargs)
def log_error_with_trace(self, message, e=None):
"""
记录带有详细追踪信息的错误日志
Args:
message: 日志消息
e: 异常对象,可选
"""
error_msg = f"{message}"
if e:
error_msg += f": {str(e)}"
# 使用traceback模块获取完整的堆栈跟踪
trace = traceback.format_exc()
self.logger.error(f"{error_msg}\n{trace}")
def log_startup(self, module_name, version=None):
"""
记录模块启动日志
Args:
module_name: 模块名称
version: 版本号,可选
"""
version_str = f" v{version}" if version else ""
self.logger.info(f"=== {module_name}{version_str} 启动完成 ===")
def log_shutdown(self, module_name):
"""
记录模块关闭日志
Args:
module_name: 模块名称
"""
self.logger.info(f"=== {module_name} 已关闭 ===")
def get_logger(name):
"""
获取指定名称的日志记录器
Args:
name (str): 日志记录器名称,通常为模块名称
Returns:
Logger: 日志记录器实例
"""
# 确保全局日志配置已初始化
setup_logger()
# 创建并返回Logger实例
return Logger(name)