主要功能: - 多用户管理系统 - 浏览器自动化(Playwright) - 任务编排和执行 - Docker容器化部署 - 数据持久化和日志管理 技术栈: - Flask 3.0.0 - Playwright 1.40.0 - SQLite with connection pooling - Docker + Docker Compose 部署说明详见README.md
306 lines
9.6 KiB
Python
Executable File
306 lines
9.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
日志管理模块
|
||
提供标准化的日志记录功能
|
||
"""
|
||
|
||
import logging
|
||
import os
|
||
from logging.handlers import RotatingFileHandler
|
||
from datetime import datetime
|
||
import threading
|
||
|
||
# 全局日志配置
|
||
_loggers = {}
|
||
_logger_lock = threading.Lock()
|
||
|
||
|
||
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):
|
||
"""格式化日志记录"""
|
||
# 添加颜色
|
||
levelname = record.levelname
|
||
if levelname in self.COLORS:
|
||
record.levelname = f"{self.COLORS[levelname]}{levelname}{self.RESET}"
|
||
|
||
# 格式化
|
||
result = super().format(record)
|
||
|
||
# 恢复原始levelname(避免影响其他handler)
|
||
record.levelname = levelname
|
||
|
||
return result
|
||
|
||
|
||
def setup_logger(name='app', level=None, log_file=None, max_bytes=10*1024*1024, backup_count=5):
|
||
"""
|
||
设置日志记录器
|
||
|
||
Args:
|
||
name: 日志器名称
|
||
level: 日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||
log_file: 日志文件路径
|
||
max_bytes: 日志文件最大大小(字节)
|
||
backup_count: 保留的备份文件数量
|
||
|
||
Returns:
|
||
logging.Logger: 配置好的日志器
|
||
"""
|
||
with _logger_lock:
|
||
# 如果已经存在,直接返回
|
||
if name in _loggers:
|
||
return _loggers[name]
|
||
|
||
# 创建日志器
|
||
logger = logging.getLogger(name)
|
||
|
||
# 设置日志级别
|
||
if level is None:
|
||
level = os.environ.get('LOG_LEVEL', 'INFO')
|
||
logger.setLevel(getattr(logging, level.upper()))
|
||
|
||
# 清除已有的处理器(避免重复)
|
||
logger.handlers.clear()
|
||
|
||
# 日志格式
|
||
detailed_formatter = logging.Formatter(
|
||
'[%(asctime)s] [%(name)s] [%(levelname)s] [%(filename)s:%(lineno)d] - %(message)s',
|
||
datefmt='%Y-%m-%d %H:%M:%S'
|
||
)
|
||
|
||
simple_formatter = logging.Formatter(
|
||
'[%(asctime)s] [%(levelname)s] %(message)s',
|
||
datefmt='%Y-%m-%d %H:%M:%S'
|
||
)
|
||
|
||
colored_formatter = ColoredFormatter(
|
||
'[%(asctime)s] [%(levelname)s] %(message)s',
|
||
datefmt='%Y-%m-%d %H:%M:%S'
|
||
)
|
||
|
||
# 控制台处理器(带颜色)
|
||
console_handler = logging.StreamHandler()
|
||
console_handler.setLevel(logging.INFO)
|
||
console_handler.setFormatter(colored_formatter)
|
||
logger.addHandler(console_handler)
|
||
|
||
# 文件处理器(如果指定了文件路径)
|
||
if log_file:
|
||
# 确保日志目录存在
|
||
log_dir = os.path.dirname(log_file)
|
||
if log_dir and not os.path.exists(log_dir):
|
||
os.makedirs(log_dir, exist_ok=True)
|
||
|
||
# 主日志文件(详细格式)
|
||
file_handler = RotatingFileHandler(
|
||
log_file,
|
||
maxBytes=max_bytes,
|
||
backupCount=backup_count,
|
||
encoding='utf-8'
|
||
)
|
||
file_handler.setLevel(logging.DEBUG)
|
||
file_handler.setFormatter(detailed_formatter)
|
||
logger.addHandler(file_handler)
|
||
|
||
# 错误日志文件(仅记录WARNING及以上)
|
||
error_file = log_file.replace('.log', '_error.log')
|
||
error_handler = RotatingFileHandler(
|
||
error_file,
|
||
maxBytes=max_bytes,
|
||
backupCount=backup_count,
|
||
encoding='utf-8'
|
||
)
|
||
error_handler.setLevel(logging.WARNING)
|
||
error_handler.setFormatter(detailed_formatter)
|
||
logger.addHandler(error_handler)
|
||
|
||
# 防止日志向上传播(避免重复)
|
||
logger.propagate = False
|
||
|
||
# 缓存日志器
|
||
_loggers[name] = logger
|
||
|
||
return logger
|
||
|
||
|
||
def get_logger(name='app'):
|
||
"""
|
||
获取日志记录器
|
||
|
||
Args:
|
||
name: 日志器名称
|
||
|
||
Returns:
|
||
logging.Logger: 日志器实例
|
||
"""
|
||
if name in _loggers:
|
||
return _loggers[name]
|
||
else:
|
||
# 如果不存在,创建一个默认的
|
||
return setup_logger(name)
|
||
|
||
|
||
class LoggerAdapter:
|
||
"""日志适配器,提供便捷的日志记录方法"""
|
||
|
||
def __init__(self, logger_name='app', context=None):
|
||
"""
|
||
初始化日志适配器
|
||
|
||
Args:
|
||
logger_name: 日志器名称
|
||
context: 上下文信息(如用户ID、账号ID等)
|
||
"""
|
||
self.logger = get_logger(logger_name)
|
||
self.context = context or {}
|
||
|
||
def _format_message(self, message):
|
||
"""格式化消息,添加上下文信息"""
|
||
if self.context:
|
||
context_str = ' '.join([f"[{k}={v}]" for k, v in self.context.items()])
|
||
return f"{context_str} {message}"
|
||
return message
|
||
|
||
def debug(self, message):
|
||
"""记录调试信息"""
|
||
self.logger.debug(self._format_message(message))
|
||
|
||
def info(self, message):
|
||
"""记录普通信息"""
|
||
self.logger.info(self._format_message(message))
|
||
|
||
def warning(self, message):
|
||
"""记录警告信息"""
|
||
self.logger.warning(self._format_message(message))
|
||
|
||
def error(self, message, exc_info=False):
|
||
"""记录错误信息"""
|
||
self.logger.error(self._format_message(message), exc_info=exc_info)
|
||
|
||
def critical(self, message, exc_info=False):
|
||
"""记录严重错误信息"""
|
||
self.logger.critical(self._format_message(message), exc_info=exc_info)
|
||
|
||
def exception(self, message):
|
||
"""记录异常信息(自动包含堆栈跟踪)"""
|
||
self.logger.exception(self._format_message(message))
|
||
|
||
|
||
class AuditLogger:
|
||
"""审计日志记录器(用于记录关键操作)"""
|
||
|
||
def __init__(self, log_file='logs/audit.log'):
|
||
"""初始化审计日志"""
|
||
self.logger = setup_logger('audit', level='INFO', log_file=log_file)
|
||
|
||
def log_user_login(self, user_id, username, ip_address, success=True):
|
||
"""记录用户登录"""
|
||
status = "成功" if success else "失败"
|
||
self.logger.info(f"用户登录{status}: user_id={user_id}, username={username}, ip={ip_address}")
|
||
|
||
def log_admin_login(self, username, ip_address, success=True):
|
||
"""记录管理员登录"""
|
||
status = "成功" if success else "失败"
|
||
self.logger.info(f"管理员登录{status}: username={username}, ip={ip_address}")
|
||
|
||
def log_user_created(self, user_id, username, created_by=None):
|
||
"""记录用户创建"""
|
||
self.logger.info(f"用户创建: user_id={user_id}, username={username}, created_by={created_by}")
|
||
|
||
def log_user_deleted(self, user_id, username, deleted_by):
|
||
"""记录用户删除"""
|
||
self.logger.warning(f"用户删除: user_id={user_id}, username={username}, deleted_by={deleted_by}")
|
||
|
||
def log_password_reset(self, user_id, username, reset_by):
|
||
"""记录密码重置"""
|
||
self.logger.warning(f"密码重置: user_id={user_id}, username={username}, reset_by={reset_by}")
|
||
|
||
def log_config_change(self, config_name, old_value, new_value, changed_by):
|
||
"""记录配置修改"""
|
||
self.logger.warning(f"配置修改: {config_name} 从 {old_value} 改为 {new_value}, changed_by={changed_by}")
|
||
|
||
def log_security_event(self, event_type, description, ip_address=None):
|
||
"""记录安全事件"""
|
||
self.logger.warning(f"安全事件 [{event_type}]: {description}, ip={ip_address}")
|
||
|
||
|
||
# 全局审计日志实例
|
||
audit_logger = AuditLogger()
|
||
|
||
|
||
# 辅助函数
|
||
def log_exception(logger, message="发生异常"):
|
||
"""记录异常(包含堆栈跟踪)"""
|
||
if isinstance(logger, str):
|
||
logger = get_logger(logger)
|
||
logger.exception(message)
|
||
|
||
|
||
def log_performance(logger, operation, duration_ms, threshold_ms=1000):
|
||
"""记录性能信息"""
|
||
if isinstance(logger, str):
|
||
logger = get_logger(logger)
|
||
|
||
if duration_ms > threshold_ms:
|
||
logger.warning(f"性能警告: {operation} 耗时 {duration_ms}ms (阈值: {threshold_ms}ms)")
|
||
else:
|
||
logger.debug(f"性能: {operation} 耗时 {duration_ms}ms")
|
||
|
||
|
||
# 初始化默认日志器
|
||
def init_logging(log_level='INFO', log_file='logs/app.log'):
|
||
"""
|
||
初始化日志系统
|
||
|
||
Args:
|
||
log_level: 日志级别
|
||
log_file: 日志文件路径
|
||
"""
|
||
# 创建主应用日志器
|
||
setup_logger('app', level=log_level, log_file=log_file)
|
||
|
||
# 创建数据库日志器
|
||
setup_logger('database', level=log_level, log_file='logs/database.log')
|
||
|
||
# 创建自动化日志器
|
||
setup_logger('automation', level=log_level, log_file='logs/automation.log')
|
||
|
||
# 创建审计日志器(已在AuditLogger中创建)
|
||
|
||
print("✓ 日志系统初始化完成")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
# 测试日志系统
|
||
init_logging(log_level='DEBUG')
|
||
|
||
logger = get_logger('app')
|
||
logger.debug("这是调试信息")
|
||
logger.info("这是普通信息")
|
||
logger.warning("这是警告信息")
|
||
logger.error("这是错误信息")
|
||
logger.critical("这是严重错误信息")
|
||
|
||
# 测试上下文日志
|
||
adapter = LoggerAdapter('app', {'user_id': 123, 'username': 'test'})
|
||
adapter.info("用户操作日志")
|
||
|
||
# 测试审计日志
|
||
audit_logger.log_user_login(123, 'test_user', '127.0.0.1', success=True)
|
||
audit_logger.log_security_event('LOGIN_ATTEMPT', '多次登录失败', '192.168.1.1')
|
||
|
||
print("\n日志测试完成,请检查 logs/ 目录")
|