208 lines
7.0 KiB
Python
208 lines
7.0 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Wolf Chat 批次記憶備份工具
|
||
|
||
自動掃描chat_logs資料夾,針對所有日誌檔案執行記憶備份
|
||
"""
|
||
|
||
import os
|
||
import re
|
||
import sys
|
||
import time
|
||
import argparse
|
||
import subprocess
|
||
import logging
|
||
from datetime import datetime
|
||
from typing import List, Optional, Tuple
|
||
|
||
# 設置日誌
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||
handlers=[
|
||
logging.FileHandler("batch_backup.log"),
|
||
logging.StreamHandler()
|
||
]
|
||
)
|
||
logger = logging.getLogger("BatchMemoryBackup")
|
||
|
||
def find_log_files(log_dir: str = "chat_logs") -> List[Tuple[str, str]]:
|
||
"""
|
||
掃描指定目錄,找出所有符合YYYY-MM-DD.log格式的日誌文件
|
||
|
||
返回: [(日期字符串, 文件路徑), ...],按日期排序
|
||
"""
|
||
date_pattern = re.compile(r'^(\d{4}-\d{2}-\d{2})\.log$')
|
||
log_files = []
|
||
|
||
# 確保目錄存在
|
||
if not os.path.exists(log_dir) or not os.path.isdir(log_dir):
|
||
logger.error(f"目錄不存在或不是有效目錄: {log_dir}")
|
||
return []
|
||
|
||
# 掃描目錄
|
||
for filename in os.listdir(log_dir):
|
||
match = date_pattern.match(filename)
|
||
if match:
|
||
date_str = match.group(1)
|
||
file_path = os.path.join(log_dir, filename)
|
||
try:
|
||
# 驗證日期格式
|
||
datetime.strptime(date_str, "%Y-%m-%d")
|
||
log_files.append((date_str, file_path))
|
||
except ValueError:
|
||
logger.warning(f"發現無效的日期格式: {filename}")
|
||
|
||
# 按日期排序
|
||
log_files.sort(key=lambda x: x[0])
|
||
return log_files
|
||
|
||
def process_log_file(date_str: str, backup_script: str = "memory_backup.py") -> bool:
|
||
"""
|
||
為指定日期的日誌文件執行記憶備份
|
||
|
||
Parameters:
|
||
date_str: 日期字符串,格式為YYYY-MM-DD
|
||
backup_script: 備份腳本路徑
|
||
|
||
Returns:
|
||
bool: 操作是否成功
|
||
"""
|
||
logger.info(f"開始處理日期 {date_str} 的日誌")
|
||
|
||
try:
|
||
# 構建命令
|
||
cmd = [sys.executable, backup_script, "--backup", "--date", date_str]
|
||
|
||
# 執行命令
|
||
logger.info(f"執行命令: {' '.join(cmd)}")
|
||
process = subprocess.run(
|
||
cmd,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
check=False # 不要在命令失敗時拋出異常
|
||
)
|
||
|
||
# 檢查結果
|
||
if process.returncode == 0:
|
||
logger.info(f"日期 {date_str} 的處理完成")
|
||
return True
|
||
else:
|
||
logger.error(f"處理日期 {date_str} 失敗: {process.stderr}")
|
||
return False
|
||
|
||
except Exception as e:
|
||
logger.error(f"處理日期 {date_str} 時發生異常: {str(e)}")
|
||
return False
|
||
|
||
def batch_process(log_dir: str = "chat_logs", backup_script: str = "memory_backup.py",
|
||
date_range: Optional[Tuple[str, str]] = None,
|
||
wait_seconds: int = 5) -> Tuple[int, int]:
|
||
"""
|
||
批次處理多個日誌文件
|
||
|
||
Parameters:
|
||
log_dir: 日誌目錄路徑
|
||
backup_script: 備份腳本路徑
|
||
date_range: (開始日期, 結束日期),用於限制處理範圍,格式為YYYY-MM-DD
|
||
wait_seconds: 每個文件處理後的等待時間(秒)
|
||
|
||
Returns:
|
||
(成功數量, 總數量)
|
||
"""
|
||
log_files = find_log_files(log_dir)
|
||
|
||
if not log_files:
|
||
logger.warning(f"在 {log_dir} 中未找到有效的日誌文件")
|
||
return (0, 0)
|
||
|
||
logger.info(f"找到 {len(log_files)} 個日誌文件")
|
||
|
||
# 如果指定了日期範圍,過濾文件
|
||
if date_range:
|
||
start_date, end_date = date_range
|
||
filtered_files = [(date_str, path) for date_str, path in log_files
|
||
if start_date <= date_str <= end_date]
|
||
logger.info(f"根據日期範圍 {start_date} 到 {end_date} 過濾後剩餘 {len(filtered_files)} 個文件")
|
||
log_files = filtered_files
|
||
|
||
success_count = 0
|
||
total_count = len(log_files)
|
||
|
||
for i, (date_str, file_path) in enumerate(log_files):
|
||
logger.info(f"處理進度: {i+1}/{total_count} - 日期: {date_str}")
|
||
|
||
if process_log_file(date_str, backup_script):
|
||
success_count += 1
|
||
|
||
# 若不是最後一個文件,等待一段時間再處理下一個
|
||
if i < total_count - 1:
|
||
logger.info(f"等待 {wait_seconds} 秒後處理下一個文件...")
|
||
time.sleep(wait_seconds)
|
||
|
||
return (success_count, total_count)
|
||
|
||
def parse_date_arg(date_arg: str) -> Optional[str]:
|
||
"""解析日期參數,確保格式為YYYY-MM-DD"""
|
||
if not date_arg:
|
||
return None
|
||
|
||
try:
|
||
parsed_date = datetime.strptime(date_arg, "%Y-%m-%d")
|
||
return parsed_date.strftime("%Y-%m-%d")
|
||
except ValueError:
|
||
logger.error(f"無效的日期格式: {date_arg},請使用YYYY-MM-DD格式")
|
||
return None
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description='Wolf Chat 批次記憶備份工具')
|
||
parser.add_argument('--log-dir', default='chat_logs', help='日誌檔案目錄,預設為 chat_logs')
|
||
parser.add_argument('--script', default='memory_backup.py', help='記憶備份腳本路徑,預設為 memory_backup.py')
|
||
parser.add_argument('--start-date', help='開始日期(含),格式為 YYYY-MM-DD')
|
||
parser.add_argument('--end-date', help='結束日期(含),格式為 YYYY-MM-DD')
|
||
parser.add_argument('--wait', type=int, default=5, help='每個檔案處理間隔時間(秒),預設為 5 秒')
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 驗證日期參數
|
||
start_date = parse_date_arg(args.start_date)
|
||
end_date = parse_date_arg(args.end_date)
|
||
|
||
# 如果只有一個日期參數,將兩個都設為該日期(僅處理該日期)
|
||
if start_date and not end_date:
|
||
end_date = start_date
|
||
elif end_date and not start_date:
|
||
start_date = end_date
|
||
|
||
date_range = (start_date, end_date) if start_date and end_date else None
|
||
|
||
logger.info("開始批次記憶備份流程")
|
||
logger.info(f"日誌目錄: {args.log_dir}")
|
||
logger.info(f"備份腳本: {args.script}")
|
||
if date_range:
|
||
logger.info(f"日期範圍: {date_range[0]} 到 {date_range[1]}")
|
||
else:
|
||
logger.info("處理所有找到的日誌檔案")
|
||
logger.info(f"等待間隔: {args.wait} 秒")
|
||
|
||
start_time = time.time()
|
||
success, total = batch_process(
|
||
log_dir=args.log_dir,
|
||
backup_script=args.script,
|
||
date_range=date_range,
|
||
wait_seconds=args.wait
|
||
)
|
||
end_time = time.time()
|
||
|
||
duration = end_time - start_time
|
||
logger.info(f"批次處理完成。成功: {success}/{total},耗時: {duration:.2f} 秒")
|
||
|
||
if success < total:
|
||
logger.warning("部分日誌檔案處理失敗,請查看日誌瞭解詳情")
|
||
return 1
|
||
return 0
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main()) |