217 lines
7.4 KiB
Python
217 lines
7.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Flask 提示词大师 - 日志管理脚本
|
||
|
|
用于日志轮转、清理和压缩
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import gzip
|
||
|
|
import shutil
|
||
|
|
import logging
|
||
|
|
from datetime import datetime, timedelta
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
# 添加项目路径到 Python 路径
|
||
|
|
project_root = Path(__file__).parent
|
||
|
|
sys.path.insert(0, str(project_root))
|
||
|
|
|
||
|
|
def setup_logging():
|
||
|
|
"""配置日志管理日志"""
|
||
|
|
log_dir = project_root / "logs"
|
||
|
|
log_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
logging.basicConfig(
|
||
|
|
level=logging.INFO,
|
||
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
|
|
handlers=[
|
||
|
|
logging.FileHandler(log_dir / "log_manager.log", encoding='utf-8'),
|
||
|
|
logging.StreamHandler()
|
||
|
|
]
|
||
|
|
)
|
||
|
|
return logging.getLogger(__name__)
|
||
|
|
|
||
|
|
class LogManager:
|
||
|
|
"""日志管理类"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.logger = setup_logging()
|
||
|
|
self.log_dir = project_root / "logs"
|
||
|
|
self.archive_dir = self.log_dir / "archive"
|
||
|
|
self.archive_dir.mkdir(exist_ok=True)
|
||
|
|
|
||
|
|
# 日志保留策略
|
||
|
|
self.retention_days = 30 # 保留天数
|
||
|
|
self.max_file_size = 10 * 1024 * 1024 # 10MB
|
||
|
|
self.compress_files = True # 是否压缩归档文件
|
||
|
|
|
||
|
|
def rotate_logs(self):
|
||
|
|
"""日志轮转"""
|
||
|
|
try:
|
||
|
|
self.logger.info("🔄 开始日志轮转...")
|
||
|
|
|
||
|
|
# 获取所有日志文件
|
||
|
|
log_files = list(self.log_dir.glob("*.log"))
|
||
|
|
rotated_count = 0
|
||
|
|
|
||
|
|
for log_file in log_files:
|
||
|
|
if log_file.name in ['log_manager.log', 'monitor.log']:
|
||
|
|
continue # 跳过管理日志
|
||
|
|
|
||
|
|
# 检查文件大小
|
||
|
|
if log_file.stat().st_size > self.max_file_size:
|
||
|
|
# 创建归档文件名
|
||
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
|
|
archive_name = f"{log_file.stem}_{timestamp}.log"
|
||
|
|
archive_path = self.archive_dir / archive_name
|
||
|
|
|
||
|
|
# 移动文件到归档目录
|
||
|
|
shutil.move(str(log_file), str(archive_path))
|
||
|
|
|
||
|
|
# 压缩归档文件
|
||
|
|
if self.compress_files:
|
||
|
|
self.compress_file(archive_path)
|
||
|
|
|
||
|
|
self.logger.info(f"📦 轮转日志文件: {log_file.name} -> {archive_name}")
|
||
|
|
rotated_count += 1
|
||
|
|
|
||
|
|
self.logger.info(f"✅ 日志轮转完成,共轮转 {rotated_count} 个文件")
|
||
|
|
return rotated_count
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(f"❌ 日志轮转失败: {e}")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
def compress_file(self, file_path):
|
||
|
|
"""压缩文件"""
|
||
|
|
try:
|
||
|
|
compressed_path = file_path.with_suffix('.log.gz')
|
||
|
|
|
||
|
|
with open(file_path, 'rb') as f_in:
|
||
|
|
with gzip.open(compressed_path, 'wb') as f_out:
|
||
|
|
shutil.copyfileobj(f_in, f_out)
|
||
|
|
|
||
|
|
# 删除原文件
|
||
|
|
file_path.unlink()
|
||
|
|
|
||
|
|
self.logger.info(f"🗜️ 压缩文件: {file_path.name} -> {compressed_path.name}")
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(f"❌ 压缩文件失败 {file_path}: {e}")
|
||
|
|
|
||
|
|
def cleanup_old_logs(self):
|
||
|
|
"""清理旧日志"""
|
||
|
|
try:
|
||
|
|
self.logger.info("🧹 开始清理旧日志...")
|
||
|
|
|
||
|
|
cutoff_date = datetime.now() - timedelta(days=self.retention_days)
|
||
|
|
deleted_count = 0
|
||
|
|
|
||
|
|
# 清理归档目录中的旧文件
|
||
|
|
for file_path in self.archive_dir.iterdir():
|
||
|
|
if file_path.is_file():
|
||
|
|
# 获取文件修改时间
|
||
|
|
mtime = datetime.fromtimestamp(file_path.stat().st_mtime)
|
||
|
|
|
||
|
|
if mtime < cutoff_date:
|
||
|
|
file_path.unlink()
|
||
|
|
self.logger.info(f"🗑️ 删除旧日志: {file_path.name}")
|
||
|
|
deleted_count += 1
|
||
|
|
|
||
|
|
self.logger.info(f"✅ 清理完成,共删除 {deleted_count} 个旧文件")
|
||
|
|
return deleted_count
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(f"❌ 清理旧日志失败: {e}")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
def get_log_stats(self):
|
||
|
|
"""获取日志统计信息"""
|
||
|
|
try:
|
||
|
|
stats = {
|
||
|
|
'current_logs': {},
|
||
|
|
'archived_logs': {},
|
||
|
|
'total_size': 0,
|
||
|
|
'file_count': 0
|
||
|
|
}
|
||
|
|
|
||
|
|
# 当前日志文件
|
||
|
|
for log_file in self.log_dir.glob("*.log"):
|
||
|
|
if log_file.name not in ['log_manager.log', 'monitor.log']:
|
||
|
|
size = log_file.stat().st_size
|
||
|
|
stats['current_logs'][log_file.name] = {
|
||
|
|
'size': size,
|
||
|
|
'size_mb': size / 1024 / 1024
|
||
|
|
}
|
||
|
|
stats['total_size'] += size
|
||
|
|
stats['file_count'] += 1
|
||
|
|
|
||
|
|
# 归档日志文件
|
||
|
|
for file_path in self.archive_dir.iterdir():
|
||
|
|
if file_path.is_file():
|
||
|
|
size = file_path.stat().st_size
|
||
|
|
stats['archived_logs'][file_path.name] = {
|
||
|
|
'size': size,
|
||
|
|
'size_mb': size / 1024 / 1024
|
||
|
|
}
|
||
|
|
stats['total_size'] += size
|
||
|
|
stats['file_count'] += 1
|
||
|
|
|
||
|
|
stats['total_size_mb'] = stats['total_size'] / 1024 / 1024
|
||
|
|
|
||
|
|
return stats
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
self.logger.error(f"❌ 获取日志统计失败: {e}")
|
||
|
|
return None
|
||
|
|
|
||
|
|
def print_stats(self):
|
||
|
|
"""打印日志统计信息"""
|
||
|
|
stats = self.get_log_stats()
|
||
|
|
if not stats:
|
||
|
|
return
|
||
|
|
|
||
|
|
print("日志统计信息:")
|
||
|
|
print(f" 总文件数: {stats['file_count']}")
|
||
|
|
print(f" 总大小: {stats['total_size_mb']:.2f}MB")
|
||
|
|
print()
|
||
|
|
|
||
|
|
if stats['current_logs']:
|
||
|
|
print("当前日志文件:")
|
||
|
|
for name, info in stats['current_logs'].items():
|
||
|
|
print(f" {name}: {info['size_mb']:.2f}MB")
|
||
|
|
print()
|
||
|
|
|
||
|
|
if stats['archived_logs']:
|
||
|
|
print("归档日志文件:")
|
||
|
|
for name, info in stats['archived_logs'].items():
|
||
|
|
print(f" {name}: {info['size_mb']:.2f}MB")
|
||
|
|
|
||
|
|
def main():
|
||
|
|
"""主函数"""
|
||
|
|
log_manager = LogManager()
|
||
|
|
|
||
|
|
if len(sys.argv) == 1:
|
||
|
|
# 默认操作:轮转和清理
|
||
|
|
log_manager.rotate_logs()
|
||
|
|
log_manager.cleanup_old_logs()
|
||
|
|
log_manager.print_stats()
|
||
|
|
elif sys.argv[1] == "rotate":
|
||
|
|
# 只进行轮转
|
||
|
|
log_manager.rotate_logs()
|
||
|
|
elif sys.argv[1] == "cleanup":
|
||
|
|
# 只进行清理
|
||
|
|
log_manager.cleanup_old_logs()
|
||
|
|
elif sys.argv[1] == "stats":
|
||
|
|
# 显示统计信息
|
||
|
|
log_manager.print_stats()
|
||
|
|
else:
|
||
|
|
print("用法:")
|
||
|
|
print(" python log_manager.py # 轮转和清理")
|
||
|
|
print(" python log_manager.py rotate # 只轮转")
|
||
|
|
print(" python log_manager.py cleanup # 只清理")
|
||
|
|
print(" python log_manager.py stats # 显示统计")
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|