diff --git a/repo/js/README.md b/repo/js/1Remote_RDP_Autoreconnection/README.md similarity index 98% rename from repo/js/README.md rename to repo/js/1Remote_RDP_Autoreconnection/README.md index 622546ac..483e9a57 100644 --- a/repo/js/README.md +++ b/repo/js/1Remote_RDP_Autoreconnection/README.md @@ -1,89 +1,89 @@ -# 📋 日志监控与自动重连脚本说明 - -**作者:火山** - -## 📌 功能介绍 -该脚本用于实时监控1Remote的日志文件,当检测到远程连接断开(日志中出现`OnRdpClientDisconnected`警告)时,自动执行重连操作,保障远程连接的稳定性。 - -### ⚡⚡⚡❗ 重要提醒:这不是JS脚本 ❗⚡⚡⚡ -### ⚡⚡⚡❗ 绝对不要不看完就直接运行 ❗⚡⚡⚡ -### ⚡⚡⚡❗ 再次强调:这不是可执行的JS脚本 ❗⚡⚡⚡ - - -## 🚀 环境要求 -- **操作系统**:Windows(脚本依赖Windows特定快捷键操作) -- **Python版本**:Python 3.6及以上 - (检查方法:按下`Win+R`输入`cmd`,在命令行中执行`python --version`) - - -## 🔧 安装依赖(重要!小白必看) -1. **以管理员身份打开命令提示符(CMD)**: - - 方法1:按下`Win + R`输入`cmd`,按住`Ctrl + Shift`同时点击「确定」 - - 方法2:开始菜单搜索「命令提示符」,右键选择「以管理员身份运行」 - -2. **执行安装命令**: - 在管理员CMD窗口中输入以下命令并回车,等待安装完成(出现`Successfully installed`字样即为成功): - ``` - pip install pyautogui watchdog - ``` - - -## 📂 文件说明 -| 文件名 | 作用描述 | -|----------------------|--------------------------------------------------------------------------| -| `log_monitor.py` | 核心监控程序,负责监控日志文件变化、检测断开事件并触发重连操作 | -| `autoreconnection.py`| 重连操作脚本,通过模拟键盘快捷键(Win+D、Alt+M等)执行自动重连 | -| `config.json` | 配置文件,存储1Remote日志文件所在目录(示例:`C:\...\1Remote-...\.logs`)| -| `监控异常.txt` | 程序运行日志,记录操作过程、错误信息及重连状态(用于问题排查) | - - -## 🔍 首次使用步骤 -1. 确保已安装Python 3.6及以上版本并完成依赖安装 -2. 双击运行`log_monitor.py`或在CMD中执行: - ``` - python log_monitor.py - ``` -3. 首次运行将引导配置日志目录: - - 日志文件命名格式为 `1Remote.log_YYYYMMDD.md`(按日期生成) - - 请输入1Remote日志所在目录的完整路径(例如 `.logs` 文件夹路径) - - -## ⚙️ 配置说明 -配置信息存储在`config.json`中,格式如下: -```json -{ - "log_dir": "C:\\Users\\Administrator\\Desktop\\1Remote-1.2.0-net9-x64\\.logs" -} -``` -- **修改日志目录**: - 1. 运行`log_monitor.py`,程序会引导配置 - 2. 或删除`config.json`后重新运行`log_monitor.py`,程序会重新引导配置 - - -## 🖥️ 使用方法 -1. 运行`log_monitor.py`后,程序将自动监控日志文件 -2. 当检测到连接断开时,自动执行以下操作(通过`autoreconnection.py`实现): - - 按`Win+D`返回桌面 - - 按`Alt+M`组合键(按住Alt键后按M键,保持0.5秒后松开) - - 连续按两次回车键(间隔0.3秒) -3. 运行状态会同时显示在控制台和`监控异常.txt`中 - - -## ⚠️ 注意事项 -1. 确保1Remote程序正常运行且日志文件能正常生成(否则会提示"未找到日志文件") -2. 脚本运行时会模拟键盘操作,建议避免手动操作鼠标键盘,以免干扰流程 -3. 重连逻辑: - - 最多尝试3次重连,失败则暂停重试 - - 单次重连超时时间为30秒,20秒内未检测到成功标志会自动重试 -4. 支持跨天日志文件切换(因日志按日期命名) -5. 所有操作记录(含错误信息)均保存在`监控异常.txt`,便于排查问题 -6. 确保Alt+M的1Remote快捷指令能够正常执行,建议先双击`autoreconnection.py`查看效果 -7. 确保Win+D的Windows快捷键能指向正确是显示器(双屏会多一步选择哪块屏幕的操作,建议问AI修改一下`autoreconnection.py`中的操作) - - -## ❓ 常见问题 -| 问题现象 | 解决方法 | -|---------------------------|--------------------------------------------------------------------------| -| 提示"未找到日志文件" | 1. 检查`config.json`中日志目录是否正确
2. 确认1Remote已运行并生成当天日志(`1Remote.log_YYYYMMDD.md`) | -| 重连操作无反应 | 1. 检查`autoreconnection.py`是否与`log_monitor.py`在同一目录
2. 手动测试快捷键(Win+D、Alt+M)是否有效
3. 重新执行管理员CMD下的依赖安装命令 | +# 📋 日志监控与自动重连脚本说明 + +**作者:火山** + +## 📌 功能介绍 +该脚本用于实时监控1Remote的日志文件,当检测到远程连接断开(日志中出现`OnRdpClientDisconnected`警告)时,自动执行重连操作,保障远程连接的稳定性。 + +### ⚡⚡⚡❗ 重要提醒:这不是JS脚本 ❗⚡⚡⚡ +### ⚡⚡⚡❗ 绝对不要不看完就直接运行 ❗⚡⚡⚡ +### ⚡⚡⚡❗ 再次强调:这不是可执行的JS脚本 ❗⚡⚡⚡ + + +## 🚀 环境要求 +- **操作系统**:Windows(脚本依赖Windows特定快捷键操作) +- **Python版本**:Python 3.6及以上 + (检查方法:按下`Win+R`输入`cmd`,在命令行中执行`python --version`) + + +## 🔧 安装依赖(重要!小白必看) +1. **以管理员身份打开命令提示符(CMD)**: + - 方法1:按下`Win + R`输入`cmd`,按住`Ctrl + Shift`同时点击「确定」 + - 方法2:开始菜单搜索「命令提示符」,右键选择「以管理员身份运行」 + +2. **执行安装命令**: + 在管理员CMD窗口中输入以下命令并回车,等待安装完成(出现`Successfully installed`字样即为成功): + ``` + pip install pyautogui watchdog + ``` + + +## 📂 文件说明 +| 文件名 | 作用描述 | +|----------------------|--------------------------------------------------------------------------| +| `log_monitor.py` | 核心监控程序,负责监控日志文件变化、检测断开事件并触发重连操作 | +| `autoreconnection.py`| 重连操作脚本,通过模拟键盘快捷键(Win+D、Alt+M等)执行自动重连 | +| `config.json` | 配置文件,存储1Remote日志文件所在目录(示例:`C:\...\1Remote-...\.logs`)| +| `监控异常.txt` | 程序运行日志,记录操作过程、错误信息及重连状态(用于问题排查) | + + +## 🔍 首次使用步骤 +1. 确保已安装Python 3.6及以上版本并完成依赖安装 +2. 双击运行`log_monitor.py`或在CMD中执行: + ``` + python log_monitor.py + ``` +3. 首次运行将引导配置日志目录: + - 日志文件命名格式为 `1Remote.log_YYYYMMDD.md`(按日期生成) + - 请输入1Remote日志所在目录的完整路径(例如 `.logs` 文件夹路径) + + +## ⚙️ 配置说明 +配置信息存储在`config.json`中,格式如下: +```json +{ + "log_dir": "C:\\Users\\Administrator\\Desktop\\1Remote-1.2.0-net9-x64\\.logs" +} +``` +- **修改日志目录**: + 1. 运行`log_monitor.py`,程序会引导配置 + 2. 或删除`config.json`后重新运行`log_monitor.py`,程序会重新引导配置 + + +## 🖥️ 使用方法 +1. 运行`log_monitor.py`后,程序将自动监控日志文件 +2. 当检测到连接断开时,自动执行以下操作(通过`autoreconnection.py`实现): + - 按`Win+D`返回桌面 + - 按`Alt+M`组合键(按住Alt键后按M键,保持0.5秒后松开) + - 连续按两次回车键(间隔0.3秒) +3. 运行状态会同时显示在控制台和`监控异常.txt`中 + + +## ⚠️ 注意事项 +1. 确保1Remote程序正常运行且日志文件能正常生成(否则会提示"未找到日志文件") +2. 脚本运行时会模拟键盘操作,建议避免手动操作鼠标键盘,以免干扰流程 +3. 重连逻辑: + - 最多尝试3次重连,失败则暂停重试 + - 单次重连超时时间为30秒,20秒内未检测到成功标志会自动重试 +4. 支持跨天日志文件切换(因日志按日期命名) +5. 所有操作记录(含错误信息)均保存在`监控异常.txt`,便于排查问题 +6. 确保Alt+M的1Remote快捷指令能够正常执行,建议先双击`autoreconnection.py`查看效果 +7. 确保Win+D的Windows快捷键能指向正确是显示器(双屏会多一步选择哪块屏幕的操作,建议问AI修改一下`autoreconnection.py`中的操作) + + +## ❓ 常见问题 +| 问题现象 | 解决方法 | +|---------------------------|--------------------------------------------------------------------------| +| 提示"未找到日志文件" | 1. 检查`config.json`中日志目录是否正确
2. 确认1Remote已运行并生成当天日志(`1Remote.log_YYYYMMDD.md`) | +| 重连操作无反应 | 1. 检查`autoreconnection.py`是否与`log_monitor.py`在同一目录
2. 手动测试快捷键(Win+D、Alt+M)是否有效
3. 重新执行管理员CMD下的依赖安装命令 | | 程序启动失败 | 1. 查看`监控异常.txt`获取错误详情
2. 检查Python版本是否符合要求(3.6及以上) | \ No newline at end of file diff --git a/repo/js/autoreconnection.py b/repo/js/1Remote_RDP_Autoreconnection/autoreconnection.py similarity index 96% rename from repo/js/autoreconnection.py rename to repo/js/1Remote_RDP_Autoreconnection/autoreconnection.py index 93b24292..82b75283 100644 --- a/repo/js/autoreconnection.py +++ b/repo/js/1Remote_RDP_Autoreconnection/autoreconnection.py @@ -1,24 +1,24 @@ -import pyautogui -import time - -print("正在执行会话关闭后操作...") -time.sleep(2) -# 返回Windows桌面(Win+D) -pyautogui.hotkey('winleft', 'd') # 更通用的Win+D组合键 -time.sleep(1) # 等待1秒 - -# 按下Alt+M键(半秒后松开) -pyautogui.keyDown('alt') # 按住Alt键 -pyautogui.press('m') # 按M键 -time.sleep(0.5) # 精确等待0.5秒(半秒) -pyautogui.keyUp('alt') # 松开Alt键 -pyautogui.keyUp('m') # 松开M键(确保按键无粘连) - -time.sleep(0.1) # 等待0.1秒 - -# 按两次回车键,间隔0.3秒 -pyautogui.press('enter') -time.sleep(0.3) # 等待0.3秒 -pyautogui.press('enter') - +import pyautogui +import time + +print("正在执行会话关闭后操作...") +time.sleep(2) +# 返回Windows桌面(Win+D) +pyautogui.hotkey('winleft', 'd') # 更通用的Win+D组合键 +time.sleep(1) # 等待1秒 + +# 按下Alt+M键(半秒后松开) +pyautogui.keyDown('alt') # 按住Alt键 +pyautogui.press('m') # 按M键 +time.sleep(0.5) # 精确等待0.5秒(半秒) +pyautogui.keyUp('alt') # 松开Alt键 +pyautogui.keyUp('m') # 松开M键(确保按键无粘连) + +time.sleep(0.1) # 等待0.1秒 + +# 按两次回车键,间隔0.3秒 +pyautogui.press('enter') +time.sleep(0.3) # 等待0.3秒 +pyautogui.press('enter') + print("操作完成!") \ No newline at end of file diff --git a/repo/js/log_monitor.py b/repo/js/1Remote_RDP_Autoreconnection/log_monitor.py similarity index 97% rename from repo/js/log_monitor.py rename to repo/js/1Remote_RDP_Autoreconnection/log_monitor.py index e2d402df..82b7e045 100644 --- a/repo/js/log_monitor.py +++ b/repo/js/1Remote_RDP_Autoreconnection/log_monitor.py @@ -1,368 +1,368 @@ -import os -import sys -import time -import json -import logging -import subprocess -from datetime import datetime -from watchdog.observers import Observer -from watchdog.events import FileSystemEventHandler - -# 配置参数 -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -CONFIG_FILE = os.path.join(SCRIPT_DIR, "config.json") -LOG_MONITOR_PATH = os.path.join(SCRIPT_DIR, "监控异常.txt") -ENCODING = "utf-8" -RECONNECT_SCRIPT_NAME = "autoreconnection.py" - -# 创建日志目录(如果不存在) -os.makedirs(SCRIPT_DIR, exist_ok=True) - -# 初始化日志系统 -logging.basicConfig( - filename=LOG_MONITOR_PATH, - level=logging.INFO, - format='%(asctime)s [%(levelname)s] %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' -) -# 同时输出到控制台 -console = logging.StreamHandler() -console.setLevel(logging.INFO) -formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s') -console.setFormatter(formatter) -logging.getLogger().addHandler(console) - -# 记录状态 -last_position = 0 -processed_times = {} -last_processed_date = None -reconnect_count = 0 -current_log_file = None - -# 重启验证相关变量 -reconnect_attempts = 0 # 当前重启尝试次数 -max_reconnect_attempts = 3 # 最大尝试次数 -reconnect_in_progress = False # 标记是否有重启操作正在进行 -reconnect_start_time = 0 # 重启开始时间戳 -reconnect_success_detected = False # 是否检测到成功重启的日志 - -def configure_log_directory(): - """引导用户配置日志目录""" - print("="*50) - print("日志监控程序配置") - print("="*50) - print("请指定1Remote日志文件所在的目录") - print("日志文件命名格式为: 1Remote.log_YYYYMMDD.md") - print("="*50) - - while True: - log_dir = input("请输入日志目录完整路径: ").strip() - - # 验证路径是否存在 - if not os.path.exists(log_dir): - print(f"错误: 路径不存在 - {log_dir}") - continue - - # 验证是否是目录 - if not os.path.isdir(log_dir): - print(f"错误: 这不是一个目录 - {log_dir}") - continue - - # 验证目录中是否有日志文件 - log_files = [f for f in os.listdir(log_dir) - if f.startswith("1Remote.log_") and f.endswith(".md")] - - if not log_files: - print(f"警告: 目录中没有找到日志文件 - {log_dir}") - choice = input("是否继续使用此目录? (y/n): ").strip().lower() - if choice != 'y': - continue - - return log_dir - -def load_config(): - """从配置文件加载日志目录""" - if not os.path.exists(CONFIG_FILE): - return None - - try: - with open(CONFIG_FILE, 'r', encoding=ENCODING) as f: - config = json.load(f) - log_dir = config.get('log_dir') - - # 验证目录是否存在 - if not os.path.isdir(log_dir): - logging.error(f"配置文件中的目录不存在: {log_dir}") - return None - - return log_dir - - except (json.JSONDecodeError, UnicodeDecodeError): - logging.error("配置文件损坏,请重新配置") - return None - except Exception as e: - logging.error(f"加载配置失败: {str(e)}") - return None - -def save_config(log_dir): - """保存日志目录到配置文件""" - config = {'log_dir': log_dir} - try: - with open(CONFIG_FILE, 'w', encoding=ENCODING) as f: - json.dump(config, f, ensure_ascii=False, indent=4) - logging.info(f"配置已保存到: {CONFIG_FILE}") - return True - except Exception as e: - logging.error(f"保存配置失败: {str(e)}") - return False - -def get_today_log_file(log_dir): - """获取指定目录下当天的日志文件""" - today = datetime.now().strftime("%Y%m%d") - file_pattern = f"1Remote.log_{today}.md" - - # 查找符合日期模式的文件 - for entry in os.scandir(log_dir): - if entry.is_file() and entry.name == file_pattern: - return entry.path - - return None - -def execute_reconnect(): - """执行重连操作""" - global reconnect_count, reconnect_attempts, reconnect_in_progress, reconnect_start_time, reconnect_success_detected - - # 如果已经达到最大尝试次数,重置并返回失败 - if reconnect_attempts >= max_reconnect_attempts: - logging.warning(f"【达到最大重试次数】已尝试{max_reconnect_attempts}次重启,停止重试") - reconnect_attempts = 0 - reconnect_in_progress = False - return False - - try: - reconnect_script_path = os.path.join(SCRIPT_DIR, RECONNECT_SCRIPT_NAME) - - # 记录开始时间 - start_time = time.time() - # 调用外部重启脚本 - result = subprocess.run( - [sys.executable, reconnect_script_path], - check=True, - capture_output=True, - text=True, - timeout=30 - ) - # 计算耗时 - elapsed = time.time() - start_time - - # 重置成功标记,设置重启状态 - reconnect_success_detected = False - reconnect_in_progress = True - reconnect_start_time = time.time() - reconnect_attempts += 1 - reconnect_count += 1 - - logging.info(f"【重启尝试 {reconnect_attempts}/{max_reconnect_attempts}】执行成功,耗时:{elapsed:.2f}s,总重启次数:{reconnect_count}") - return True - except subprocess.CalledProcessError as e: - logging.error(f"【重启失败】错误代码:{e.returncode}\nstdout: {e.stdout.strip()}\nstderr: {e.stderr.strip()}") - except FileNotFoundError: - logging.error(f"【重启失败】未找到重启脚本:{reconnect_script_path}") - except subprocess.TimeoutExpired: - logging.error(f"【重启失败】执行超时(超过30秒)") - except Exception as e: - logging.error(f"【重启异常】未知错误:{str(e)}", exc_info=True) - - reconnect_attempts += 1 - logging.warning(f"【重启尝试 {reconnect_attempts}/{max_reconnect_attempts}】失败") - return False - -def process_log_line(line): - """处理日志行并检查是否需要重启及重启是否成功""" - global last_position, processed_times, reconnect_in_progress, reconnect_success_detected, reconnect_attempts - - # 检查是否是重启成功的标志日志 - if reconnect_in_progress and 'Warning' in line and '[AxMsRdpClient09Host.cs(ReConn:42)]' in line and 'RDP Host: Call ReConn' in line: - logging.info(f"【重启成功验证】检测到成功标志: {line}") - reconnect_success_detected = True - reconnect_in_progress = False - reconnect_attempts = 0 # 重置尝试次数 - return True - - # 原有逻辑:检查是否需要重启 - if 'Warning' in line: - if 'OnRdpClientDisconnected' in line and not reconnect_in_progress: - logging.warning(f"【触发重启】{line}") - return execute_reconnect() - elif 'ReConn:42' in line: - logging.info(f"【忽略信号】{line}") - else: - logging.info(f"【未知警告】{line}") - - # 时间戳处理 - try: - if '[' in line and ']' in line: - timestamp_str = line.split(']')[0][1:] - log_time = datetime.strptime(timestamp_str, "%H:%M:%S.%f").time() - - time_key = timestamp_str.replace(':', '').replace('.', '') - if time_key not in processed_times: - processed_times[time_key] = True - logging.info(f"【新警告记录】{line}") - except ValueError: - logging.error(f"【无效时间戳】{line}") - - return False - -def monitor_log_file(log_file): - """监控指定的日志文件""" - global last_position, processed_times, last_processed_date - - try: - # 检查是否为新的一天 - current_date = datetime.now().date() - if current_date != last_processed_date: - processed_times.clear() - last_processed_date = current_date - logging.info(f"【新的一天】开始监控{current_date}的日志文件") - - # 获取文件大小 - file_size = os.path.getsize(log_file) - - # 从上次结束位置读取新内容 - with open(log_file, 'r', encoding=ENCODING) as f: - if file_size < last_position: - logging.warning("【文件被截断或重置】重新从头开始读取") - f.seek(0) - else: - f.seek(last_position) - new_lines = f.read().splitlines() - - # 处理新增内容 - for line in new_lines: - process_log_line(line) - - # 更新最后读取位置 - last_position = file_size - - except Exception as e: - logging.error(f"【监控异常】{str(e)}", exc_info=True) - -class LogFileHandler(FileSystemEventHandler): - """处理日志文件变化事件""" - def __init__(self, log_dir): - self.log_dir = log_dir - - def on_modified(self, event): - global current_log_file - - if not event.is_directory: - # 检查是否是当天日志文件 - today_file = get_today_log_file(self.log_dir) - if today_file and event.src_path == today_file: - monitor_log_file(today_file) - -def start_monitoring(log_dir): - """启动日志监控(静默等待日志文件生成)""" - global current_log_file, last_position, reconnect_in_progress, reconnect_start_time, reconnect_success_detected, reconnect_attempts - - # 验证监控目录是否有效(仅在启动时检查一次) - if not os.path.isdir(log_dir): - logging.error(f"【错误】监控目录不存在或无效: {log_dir}") - return False - - try: - while True: # 外层循环:处理跨天切换 - # 静默等待当天日志文件生成(每5秒检查一次) - while True: - current_log_file = get_today_log_file(log_dir) - if current_log_file: - logging.info(f"【找到日志文件】开始监控: {current_log_file}") - last_position = os.path.getsize(current_log_file) # 初始化读取位置 - break - # 无日志文件时不输出错误,仅静默等待 - time.sleep(5) - - # 创建事件处理器和观察器 - event_handler = LogFileHandler(log_dir) - observer = Observer() - observer.schedule(event_handler, path=log_dir, recursive=False) - observer.start() - - # 监控循环:每1秒检查一次是否跨天或重启超时 - try: - while True: - # 检查重启超时 - if reconnect_in_progress: - elapsed_time = time.time() - reconnect_start_time - if elapsed_time > 20: # 超过20秒 - if not reconnect_success_detected: - logging.warning(f"【重启验证超时】20秒内未检测到成功标志,尝试重新启动") - # 停止当前监控,准备重试 - observer.stop() - observer.join() - - # 如果未达到最大尝试次数,重新执行重启 - if reconnect_attempts < max_reconnect_attempts: - execute_reconnect() - else: - # 达到最大尝试次数,重置状态 - reconnect_in_progress = False - reconnect_attempts = 0 - - # 重新启动监控 - event_handler = LogFileHandler(log_dir) - observer = Observer() - observer.schedule(event_handler, path=log_dir, recursive=False) - observer.start() - else: - # 已检测到成功标志,重置状态 - reconnect_in_progress = False - - # 检查是否跨天 - today_file = get_today_log_file(log_dir) - if today_file != current_log_file: - # 跨天:停止当前监控,进入外层循环等待新文件 - observer.stop() - observer.join() - logging.info(f"【跨天切换】当前日志文件已过期,等待新日志文件...") - break - - time.sleep(1) # 缩短检查间隔为1秒,提高响应速度 - except KeyboardInterrupt: - observer.stop() - observer.join() - return True - observer.join() - except Exception as e: - logging.error(f"【监控异常】{str(e)}", exc_info=True) - return False - -if __name__ == "__main__": - print("="*50) - print("日志监控程序") - print("作者:火山") - print("="*50) - - # 尝试加载配置 - log_watch_dir = load_config() - - if not log_watch_dir: - log_watch_dir = configure_log_directory() - if not save_config(log_watch_dir): - print("配置保存失败,程序退出") - sys.exit(1) - - print("="*50) - print(f"监控目录: {log_watch_dir}") - # 修改状态提示文本,更准确反映程序状态 - print("正在监控中(等待当天日志文件生成)...") - - try: - start_monitoring(log_watch_dir) - print("监控已成功启动") - print("作者:火山") - except Exception as e: - logging.error(f"【启动失败】{str(e)}", exc_info=True) - print(f"监控启动失败: {str(e)}") +import os +import sys +import time +import json +import logging +import subprocess +from datetime import datetime +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler + +# 配置参数 +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +CONFIG_FILE = os.path.join(SCRIPT_DIR, "config.json") +LOG_MONITOR_PATH = os.path.join(SCRIPT_DIR, "监控异常.txt") +ENCODING = "utf-8" +RECONNECT_SCRIPT_NAME = "autoreconnection.py" + +# 创建日志目录(如果不存在) +os.makedirs(SCRIPT_DIR, exist_ok=True) + +# 初始化日志系统 +logging.basicConfig( + filename=LOG_MONITOR_PATH, + level=logging.INFO, + format='%(asctime)s [%(levelname)s] %(message)s', + datefmt='%Y-%m-%d %H:%M:%S' +) +# 同时输出到控制台 +console = logging.StreamHandler() +console.setLevel(logging.INFO) +formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s') +console.setFormatter(formatter) +logging.getLogger().addHandler(console) + +# 记录状态 +last_position = 0 +processed_times = {} +last_processed_date = None +reconnect_count = 0 +current_log_file = None + +# 重启验证相关变量 +reconnect_attempts = 0 # 当前重启尝试次数 +max_reconnect_attempts = 3 # 最大尝试次数 +reconnect_in_progress = False # 标记是否有重启操作正在进行 +reconnect_start_time = 0 # 重启开始时间戳 +reconnect_success_detected = False # 是否检测到成功重启的日志 + +def configure_log_directory(): + """引导用户配置日志目录""" + print("="*50) + print("日志监控程序配置") + print("="*50) + print("请指定1Remote日志文件所在的目录") + print("日志文件命名格式为: 1Remote.log_YYYYMMDD.md") + print("="*50) + + while True: + log_dir = input("请输入日志目录完整路径: ").strip() + + # 验证路径是否存在 + if not os.path.exists(log_dir): + print(f"错误: 路径不存在 - {log_dir}") + continue + + # 验证是否是目录 + if not os.path.isdir(log_dir): + print(f"错误: 这不是一个目录 - {log_dir}") + continue + + # 验证目录中是否有日志文件 + log_files = [f for f in os.listdir(log_dir) + if f.startswith("1Remote.log_") and f.endswith(".md")] + + if not log_files: + print(f"警告: 目录中没有找到日志文件 - {log_dir}") + choice = input("是否继续使用此目录? (y/n): ").strip().lower() + if choice != 'y': + continue + + return log_dir + +def load_config(): + """从配置文件加载日志目录""" + if not os.path.exists(CONFIG_FILE): + return None + + try: + with open(CONFIG_FILE, 'r', encoding=ENCODING) as f: + config = json.load(f) + log_dir = config.get('log_dir') + + # 验证目录是否存在 + if not os.path.isdir(log_dir): + logging.error(f"配置文件中的目录不存在: {log_dir}") + return None + + return log_dir + + except (json.JSONDecodeError, UnicodeDecodeError): + logging.error("配置文件损坏,请重新配置") + return None + except Exception as e: + logging.error(f"加载配置失败: {str(e)}") + return None + +def save_config(log_dir): + """保存日志目录到配置文件""" + config = {'log_dir': log_dir} + try: + with open(CONFIG_FILE, 'w', encoding=ENCODING) as f: + json.dump(config, f, ensure_ascii=False, indent=4) + logging.info(f"配置已保存到: {CONFIG_FILE}") + return True + except Exception as e: + logging.error(f"保存配置失败: {str(e)}") + return False + +def get_today_log_file(log_dir): + """获取指定目录下当天的日志文件""" + today = datetime.now().strftime("%Y%m%d") + file_pattern = f"1Remote.log_{today}.md" + + # 查找符合日期模式的文件 + for entry in os.scandir(log_dir): + if entry.is_file() and entry.name == file_pattern: + return entry.path + + return None + +def execute_reconnect(): + """执行重连操作""" + global reconnect_count, reconnect_attempts, reconnect_in_progress, reconnect_start_time, reconnect_success_detected + + # 如果已经达到最大尝试次数,重置并返回失败 + if reconnect_attempts >= max_reconnect_attempts: + logging.warning(f"【达到最大重试次数】已尝试{max_reconnect_attempts}次重启,停止重试") + reconnect_attempts = 0 + reconnect_in_progress = False + return False + + try: + reconnect_script_path = os.path.join(SCRIPT_DIR, RECONNECT_SCRIPT_NAME) + + # 记录开始时间 + start_time = time.time() + # 调用外部重启脚本 + result = subprocess.run( + [sys.executable, reconnect_script_path], + check=True, + capture_output=True, + text=True, + timeout=30 + ) + # 计算耗时 + elapsed = time.time() - start_time + + # 重置成功标记,设置重启状态 + reconnect_success_detected = False + reconnect_in_progress = True + reconnect_start_time = time.time() + reconnect_attempts += 1 + reconnect_count += 1 + + logging.info(f"【重启尝试 {reconnect_attempts}/{max_reconnect_attempts}】执行成功,耗时:{elapsed:.2f}s,总重启次数:{reconnect_count}") + return True + except subprocess.CalledProcessError as e: + logging.error(f"【重启失败】错误代码:{e.returncode}\nstdout: {e.stdout.strip()}\nstderr: {e.stderr.strip()}") + except FileNotFoundError: + logging.error(f"【重启失败】未找到重启脚本:{reconnect_script_path}") + except subprocess.TimeoutExpired: + logging.error(f"【重启失败】执行超时(超过30秒)") + except Exception as e: + logging.error(f"【重启异常】未知错误:{str(e)}", exc_info=True) + + reconnect_attempts += 1 + logging.warning(f"【重启尝试 {reconnect_attempts}/{max_reconnect_attempts}】失败") + return False + +def process_log_line(line): + """处理日志行并检查是否需要重启及重启是否成功""" + global last_position, processed_times, reconnect_in_progress, reconnect_success_detected, reconnect_attempts + + # 检查是否是重启成功的标志日志 + if reconnect_in_progress and 'Warning' in line and '[AxMsRdpClient09Host.cs(ReConn:42)]' in line and 'RDP Host: Call ReConn' in line: + logging.info(f"【重启成功验证】检测到成功标志: {line}") + reconnect_success_detected = True + reconnect_in_progress = False + reconnect_attempts = 0 # 重置尝试次数 + return True + + # 原有逻辑:检查是否需要重启 + if 'Warning' in line: + if 'OnRdpClientDisconnected' in line and not reconnect_in_progress: + logging.warning(f"【触发重启】{line}") + return execute_reconnect() + elif 'ReConn:42' in line: + logging.info(f"【忽略信号】{line}") + else: + logging.info(f"【未知警告】{line}") + + # 时间戳处理 + try: + if '[' in line and ']' in line: + timestamp_str = line.split(']')[0][1:] + log_time = datetime.strptime(timestamp_str, "%H:%M:%S.%f").time() + + time_key = timestamp_str.replace(':', '').replace('.', '') + if time_key not in processed_times: + processed_times[time_key] = True + logging.info(f"【新警告记录】{line}") + except ValueError: + logging.error(f"【无效时间戳】{line}") + + return False + +def monitor_log_file(log_file): + """监控指定的日志文件""" + global last_position, processed_times, last_processed_date + + try: + # 检查是否为新的一天 + current_date = datetime.now().date() + if current_date != last_processed_date: + processed_times.clear() + last_processed_date = current_date + logging.info(f"【新的一天】开始监控{current_date}的日志文件") + + # 获取文件大小 + file_size = os.path.getsize(log_file) + + # 从上次结束位置读取新内容 + with open(log_file, 'r', encoding=ENCODING) as f: + if file_size < last_position: + logging.warning("【文件被截断或重置】重新从头开始读取") + f.seek(0) + else: + f.seek(last_position) + new_lines = f.read().splitlines() + + # 处理新增内容 + for line in new_lines: + process_log_line(line) + + # 更新最后读取位置 + last_position = file_size + + except Exception as e: + logging.error(f"【监控异常】{str(e)}", exc_info=True) + +class LogFileHandler(FileSystemEventHandler): + """处理日志文件变化事件""" + def __init__(self, log_dir): + self.log_dir = log_dir + + def on_modified(self, event): + global current_log_file + + if not event.is_directory: + # 检查是否是当天日志文件 + today_file = get_today_log_file(self.log_dir) + if today_file and event.src_path == today_file: + monitor_log_file(today_file) + +def start_monitoring(log_dir): + """启动日志监控(静默等待日志文件生成)""" + global current_log_file, last_position, reconnect_in_progress, reconnect_start_time, reconnect_success_detected, reconnect_attempts + + # 验证监控目录是否有效(仅在启动时检查一次) + if not os.path.isdir(log_dir): + logging.error(f"【错误】监控目录不存在或无效: {log_dir}") + return False + + try: + while True: # 外层循环:处理跨天切换 + # 静默等待当天日志文件生成(每5秒检查一次) + while True: + current_log_file = get_today_log_file(log_dir) + if current_log_file: + logging.info(f"【找到日志文件】开始监控: {current_log_file}") + last_position = os.path.getsize(current_log_file) # 初始化读取位置 + break + # 无日志文件时不输出错误,仅静默等待 + time.sleep(5) + + # 创建事件处理器和观察器 + event_handler = LogFileHandler(log_dir) + observer = Observer() + observer.schedule(event_handler, path=log_dir, recursive=False) + observer.start() + + # 监控循环:每1秒检查一次是否跨天或重启超时 + try: + while True: + # 检查重启超时 + if reconnect_in_progress: + elapsed_time = time.time() - reconnect_start_time + if elapsed_time > 20: # 超过20秒 + if not reconnect_success_detected: + logging.warning(f"【重启验证超时】20秒内未检测到成功标志,尝试重新启动") + # 停止当前监控,准备重试 + observer.stop() + observer.join() + + # 如果未达到最大尝试次数,重新执行重启 + if reconnect_attempts < max_reconnect_attempts: + execute_reconnect() + else: + # 达到最大尝试次数,重置状态 + reconnect_in_progress = False + reconnect_attempts = 0 + + # 重新启动监控 + event_handler = LogFileHandler(log_dir) + observer = Observer() + observer.schedule(event_handler, path=log_dir, recursive=False) + observer.start() + else: + # 已检测到成功标志,重置状态 + reconnect_in_progress = False + + # 检查是否跨天 + today_file = get_today_log_file(log_dir) + if today_file != current_log_file: + # 跨天:停止当前监控,进入外层循环等待新文件 + observer.stop() + observer.join() + logging.info(f"【跨天切换】当前日志文件已过期,等待新日志文件...") + break + + time.sleep(1) # 缩短检查间隔为1秒,提高响应速度 + except KeyboardInterrupt: + observer.stop() + observer.join() + return True + observer.join() + except Exception as e: + logging.error(f"【监控异常】{str(e)}", exc_info=True) + return False + +if __name__ == "__main__": + print("="*50) + print("日志监控程序") + print("作者:火山") + print("="*50) + + # 尝试加载配置 + log_watch_dir = load_config() + + if not log_watch_dir: + log_watch_dir = configure_log_directory() + if not save_config(log_watch_dir): + print("配置保存失败,程序退出") + sys.exit(1) + + print("="*50) + print(f"监控目录: {log_watch_dir}") + # 修改状态提示文本,更准确反映程序状态 + print("正在监控中(等待当天日志文件生成)...") + + try: + start_monitoring(log_watch_dir) + print("监控已成功启动") + print("作者:火山") + except Exception as e: + logging.error(f"【启动失败】{str(e)}", exc_info=True) + print(f"监控启动失败: {str(e)}") print("请检查错误日志: 监控异常.txt") \ No newline at end of file