2025-04-30 16:57:46 +08:00
|
|
|
|
# utils/logger.py
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
import traceback
|
|
|
|
|
from logging.handlers import RotatingFileHandler
|
|
|
|
|
from config.settings import LogConfig
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 延迟导入告警同步服务,避免循环导入
|
|
|
|
|
_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
|
|
|
|
|
|
2025-04-30 16:57:46 +08:00
|
|
|
|
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)
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 添加统一的文件处理器,使用基于级别的格式化器
|
2025-04-30 16:57:46 +08:00
|
|
|
|
file_handler = RotatingFileHandler(
|
|
|
|
|
LOG_CONFIG["file"],
|
|
|
|
|
maxBytes=10 * 1024 * 1024, # 10MB
|
2025-07-14 10:29:37 +08:00
|
|
|
|
backupCount=5,
|
|
|
|
|
encoding='utf-8'
|
2025-04-30 16:57:46 +08:00
|
|
|
|
)
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 使用自定义的格式化器,根据日志级别自动选择格式
|
|
|
|
|
file_handler.setFormatter(LevelBasedFormatter())
|
2025-04-30 16:57:46 +08:00
|
|
|
|
root_logger.addHandler(file_handler)
|
|
|
|
|
|
2025-07-14 10:29:37 +08:00
|
|
|
|
# 添加告警同步处理器
|
|
|
|
|
alert_sync_handler = AlertSyncHandler()
|
|
|
|
|
root_logger.addHandler(alert_sync_handler)
|
2025-04-30 16:57:46 +08:00
|
|
|
|
|
|
|
|
|
# 设置第三方库的日志级别
|
|
|
|
|
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)
|