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