手动模式
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
"""
|
||||
统一日志管理模块
|
||||
提供结构化日志记录功能,支持控制台和文件输出
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import logging.handlers
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any
|
||||
from datetime import datetime
|
||||
|
||||
from .config_manager import get_config
|
||||
|
||||
|
||||
class ColoredFormatter(logging.Formatter):
|
||||
"""带颜色的日志格式化器"""
|
||||
|
||||
# ANSI颜色代码
|
||||
COLORS = {
|
||||
'DEBUG': '\033[36m', # 青色
|
||||
'INFO': '\033[32m', # 绿色
|
||||
'WARNING': '\033[33m', # 黄色
|
||||
'ERROR': '\033[31m', # 红色
|
||||
'CRITICAL': '\033[35m', # 紫色
|
||||
}
|
||||
RESET = '\033[0m' # 重置颜色
|
||||
|
||||
def format(self, record):
|
||||
# 检查是否已经包含ANSI颜色代码
|
||||
if '\033[' in record.levelname:
|
||||
# 如果已经着色,直接返回原始格式
|
||||
return super().format(record)
|
||||
|
||||
# 添加颜色
|
||||
if record.levelname in self.COLORS:
|
||||
# 为levelname添加颜色
|
||||
colored_levelname = f"{self.COLORS[record.levelname]}{record.levelname}{self.RESET}"
|
||||
# 使用原始levelname来确定消息的颜色
|
||||
record.msg = f"{self.COLORS[record.levelname]}{record.msg}{self.RESET}"
|
||||
record.levelname = colored_levelname
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
||||
class Logger:
|
||||
"""统一日志管理器"""
|
||||
|
||||
_instance = None
|
||||
_initialized = False
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
if not self._initialized:
|
||||
self._loggers = {}
|
||||
self._config = None
|
||||
self._initialized = True
|
||||
|
||||
def init_logger(self, name: str = "gx_gp_monitor", config=None) -> logging.Logger:
|
||||
"""
|
||||
初始化日志器
|
||||
|
||||
Args:
|
||||
name: 日志器名称
|
||||
config: 配置对象,如果为None则从全局配置加载
|
||||
|
||||
Returns:
|
||||
logging.Logger: 配置好的日志器实例
|
||||
"""
|
||||
if name in self._loggers:
|
||||
return self._loggers[name]
|
||||
|
||||
# 获取配置
|
||||
if config is None:
|
||||
try:
|
||||
self._config = get_config()
|
||||
except RuntimeError:
|
||||
# 配置未加载,使用默认配置
|
||||
self._config = self._get_default_config()
|
||||
else:
|
||||
self._config = config
|
||||
|
||||
# 创建日志器
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(getattr(logging, self._config.log_level.value))
|
||||
|
||||
# 避免重复添加处理器
|
||||
if logger.handlers:
|
||||
return logger
|
||||
|
||||
# 创建格式化器
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
|
||||
# 控制台处理器
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setLevel(getattr(logging, self._config.log_level.value))
|
||||
|
||||
# 使用彩色格式化器(如果支持)
|
||||
if sys.platform != 'win32' and 'TERM' in os.environ:
|
||||
colored_formatter = ColoredFormatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
console_handler.setFormatter(colored_formatter)
|
||||
else:
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
# 文件处理器(如果配置了日志文件)
|
||||
if self._config.log_file:
|
||||
log_dir = Path(self._config.log_file).parent
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
file_handler = logging.handlers.RotatingFileHandler(
|
||||
self._config.log_file,
|
||||
maxBytes=self._config.log_max_size,
|
||||
backupCount=self._config.log_backup_count,
|
||||
encoding='utf-8'
|
||||
)
|
||||
file_handler.setLevel(getattr(logging, self._config.log_level.value))
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
self._loggers[name] = logger
|
||||
return logger
|
||||
|
||||
def _get_default_config(self):
|
||||
"""获取默认配置"""
|
||||
from .config_manager import LogLevel
|
||||
|
||||
class DefaultConfig:
|
||||
def __init__(self):
|
||||
self.log_level = LogLevel.INFO
|
||||
self.log_file = "logs/gx_gp_monitor.log"
|
||||
self.log_max_size = 10485760 # 10MB
|
||||
self.log_backup_count = 5
|
||||
|
||||
return DefaultConfig()
|
||||
|
||||
def get_logger(self, name: str = "gx_gp_monitor") -> logging.Logger:
|
||||
"""
|
||||
获取日志器
|
||||
|
||||
Args:
|
||||
name: 日志器名称
|
||||
|
||||
Returns:
|
||||
logging.Logger: 日志器实例
|
||||
"""
|
||||
if name not in self._loggers:
|
||||
return self.init_logger(name)
|
||||
return self._loggers[name]
|
||||
|
||||
def log_crawl_start(self, source_name: str, logger: Optional[logging.Logger] = None):
|
||||
"""记录爬取开始"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
logger.info(f"开始爬取 {source_name}")
|
||||
|
||||
def log_crawl_success(self, source_name: str, count: int, duration: float,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录爬取成功"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
logger.info(f"{source_name} 爬取完成,共获取 {count} 条公告,耗时 {duration:.2f}秒")
|
||||
|
||||
def log_crawl_error(self, source_name: str, error: str,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录爬取错误"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
logger.error(f"{source_name} 爬取失败: {error}")
|
||||
|
||||
def log_announcement_filtered(self, reason: str, count: int,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录公告筛选信息"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
logger.info(f"公告筛选 - {reason}: {count} 条")
|
||||
|
||||
def log_database_operation(self, operation: str, table: str, count: int = 0,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录数据库操作"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
if count > 0:
|
||||
logger.info(f"数据库操作 - {operation} {table}: {count} 条记录")
|
||||
else:
|
||||
logger.info(f"数据库操作 - {operation} {table}")
|
||||
|
||||
def log_notification_sent(self, channel: str, recipient_count: int,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录通知发送"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
logger.info(f"通知发送 - {channel}: 向 {recipient_count} 个接收者发送")
|
||||
|
||||
def log_system_metrics(self, metrics: Dict[str, Any],
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录系统指标"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
metrics_str = ", ".join([f"{k}={v}" for k, v in metrics.items()])
|
||||
logger.info(f"系统指标: {metrics_str}")
|
||||
|
||||
def log_performance_warning(self, operation: str, duration: float, threshold: float,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""记录性能警告"""
|
||||
if logger is None:
|
||||
logger = self.get_logger()
|
||||
logger.warning(f"性能警告 - {operation} 耗时 {duration:.2f}秒,超过阈值 {threshold:.2f}秒")
|
||||
|
||||
|
||||
# 全局日志管理器实例
|
||||
_logger_manager = Logger()
|
||||
|
||||
|
||||
def get_logger(name: str = "gx_gp_monitor") -> logging.Logger:
|
||||
"""
|
||||
获取日志器
|
||||
|
||||
Args:
|
||||
name: 日志器名称
|
||||
|
||||
Returns:
|
||||
logging.Logger: 日志器实例
|
||||
"""
|
||||
return _logger_manager.get_logger(name)
|
||||
|
||||
|
||||
def init_logger(name: str = "gx_gp_monitor", config=None) -> logging.Logger:
|
||||
"""
|
||||
初始化并获取日志器
|
||||
|
||||
Args:
|
||||
name: 日志器名称
|
||||
config: 配置对象
|
||||
|
||||
Returns:
|
||||
logging.Logger: 日志器实例
|
||||
"""
|
||||
return _logger_manager.init_logger(name, config)
|
||||
|
||||
|
||||
def log_function_call(func_name: str, args: Optional[Dict[str, Any]] = None,
|
||||
logger: Optional[logging.Logger] = None):
|
||||
"""
|
||||
装饰器:记录函数调用
|
||||
|
||||
Args:
|
||||
func_name: 函数名称
|
||||
args: 函数参数
|
||||
logger: 日志器实例
|
||||
"""
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
nonlocal logger
|
||||
if logger is None:
|
||||
logger = get_logger()
|
||||
|
||||
start_time = datetime.now()
|
||||
logger.debug(f"调用函数: {func_name}")
|
||||
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
duration = (datetime.now() - start_time).total_seconds()
|
||||
logger.debug(f"函数 {func_name} 执行完成,耗时 {duration:.3f}秒")
|
||||
return result
|
||||
except Exception as e:
|
||||
duration = (datetime.now() - start_time).total_seconds()
|
||||
logger.error(f"函数 {func_name} 执行失败,耗时 {duration:.3f}秒: {str(e)}")
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
# 便捷函数
|
||||
def log_info(message: str, logger: Optional[logging.Logger] = None):
|
||||
"""记录信息日志"""
|
||||
if logger is None:
|
||||
logger = get_logger()
|
||||
logger.info(message)
|
||||
|
||||
|
||||
def log_warning(message: str, logger: Optional[logging.Logger] = None):
|
||||
"""记录警告日志"""
|
||||
if logger is None:
|
||||
logger = get_logger()
|
||||
logger.warning(message)
|
||||
|
||||
|
||||
def log_error(message: str, logger: Optional[logging.Logger] = None):
|
||||
"""记录错误日志"""
|
||||
if logger is None:
|
||||
logger = get_logger()
|
||||
logger.error(message)
|
||||
|
||||
|
||||
def log_debug(message: str, logger: Optional[logging.Logger] = None):
|
||||
"""记录调试日志"""
|
||||
if logger is None:
|
||||
logger = get_logger()
|
||||
logger.debug(message)
|
||||
|
||||
|
||||
# 便捷的爬取日志记录函数
|
||||
def log_crawl_start(source_name: str):
|
||||
"""记录爬取开始"""
|
||||
_logger_manager.log_crawl_start(source_name)
|
||||
|
||||
|
||||
def log_crawl_success(source_name: str, count: int, duration: float):
|
||||
"""记录爬取成功"""
|
||||
_logger_manager.log_crawl_success(source_name, count, duration)
|
||||
|
||||
|
||||
def log_crawl_error(source_name: str, error: str):
|
||||
"""记录爬取错误"""
|
||||
_logger_manager.log_crawl_error(source_name, error)
|
||||
|
||||
|
||||
def log_announcement_filtered(reason: str, count: int):
|
||||
"""记录公告筛选信息"""
|
||||
_logger_manager.log_announcement_filtered(reason, count)
|
||||
|
||||
|
||||
def log_database_operation(operation: str, table: str, count: int = 0):
|
||||
"""记录数据库操作"""
|
||||
_logger_manager.log_database_operation(operation, table, count)
|
||||
|
||||
|
||||
def log_notification_sent(channel: str, recipient_count: int):
|
||||
"""记录通知发送"""
|
||||
_logger_manager.log_notification_sent(channel, recipient_count)
|
||||
|
||||
|
||||
def log_system_metrics(metrics: Dict[str, Any]):
|
||||
"""记录系统指标"""
|
||||
_logger_manager.log_system_metrics(metrics)
|
||||
|
||||
|
||||
def log_performance_warning(operation: str, duration: float, threshold: float):
|
||||
"""记录性能警告"""
|
||||
_logger_manager.log_performance_warning(operation, duration, threshold)
|
||||
Reference in New Issue
Block a user